Merge "DO NOT MERGE: Merge Oreo MR1 into master"
diff --git a/Android.mk b/Android.mk
index 713d082..d41e184 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,7 +17,7 @@
 LOCAL_PATH := $(call my-dir)
 
 #
-# Build app code.
+# Build rule for Launcher3 app.
 #
 include $(CLEAR_VARS)
 
@@ -26,12 +26,15 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
     android-support-v7-recyclerview \
-    android-support-v7-palette
+    android-support-v7-palette \
+    android-support-dynamic-animation
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, src_config) \
-    $(call all-proto-files-under, protos)
+    $(call all-java-files-under, src_flags) \
+    $(call all-proto-files-under, protos) \
+    $(call all-proto-files-under, proto_overrides)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
@@ -40,7 +43,7 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
 
 LOCAL_AAPT_FLAGS := \
@@ -60,14 +63,65 @@
 include $(BUILD_PACKAGE)
 
 #
+# Build rule for Launcher3 Go app for Android Go devices.
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-recyclerview \
+    android-support-v7-palette \
+    android-support-dynamic-animation
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, src_config) \
+    $(call all-java-files-under, go/src_flags) \
+    $(call all-proto-files-under, protos) \
+    $(call all-proto-files-under, proto_overrides)
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/go/res \
+    $(LOCAL_PATH)/res \
+    prebuilts/sdk/current/support/v7/recyclerview/res \
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
+
+LOCAL_AAPT_FLAGS := \
+    --auto-add-overlay \
+    --extra-packages android.support.v7.recyclerview \
+
+LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_PACKAGE_NAME := Launcher3Go
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/AndroidManifest.xml \
+    $(LOCAL_PATH)/AndroidManifest-common.xml
+
+LOCAL_MANIFEST_FILE := go/AndroidManifest.xml
+
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
+
+include $(BUILD_PACKAGE)
+
+#
 # Launcher proto buffer jar used for development
 #
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-proto-files-under, protos)
+LOCAL_SRC_FILES := $(call all-proto-files-under, protos) $(call all-proto-files-under, proto_overrides)
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
 
 LOCAL_MODULE_TAGS := optional
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index ab582fe..ad404c0 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -86,6 +86,12 @@
             android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service
+            android:name="com.android.launcher3.compat.WallpaperManagerCompatVL$ColorExtractionService"
+            android:exported="false"
+            android:process=":wallpaper_chooser"
+            android:permission="android.permission.BIND_JOB_SERVICE" />
+
         <service android:name="com.android.launcher3.notification.NotificationListener"
                  android:enabled="@bool/notification_badging_enabled"
                  android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bcb522b..23bddf6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -70,7 +70,7 @@
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:windowSoftInputMode="adjustPan|stateUnchanged"
+            android:windowSoftInputMode="adjustPan"
             android:screenOrientation="nosensor"
             android:configChanges="keyboard|keyboardHidden|navigation"
             android:resizeableActivity="true"
@@ -82,6 +82,7 @@
                 <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>
 
diff --git a/build.gradle b/build.gradle
index 9c71693..886ccac 100644
--- a/build.gradle
+++ b/build.gradle
@@ -35,28 +35,49 @@
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
+
+        l3go {
+            applicationId 'com.android.launcher3'
+            testApplicationId 'com.android.launcher3.tests'
+        }
     }
     sourceSets {
         main {
             res.srcDirs = ['res']
-            java.srcDirs = ['src', 'src_config']
+            java.srcDirs = ['src']
             manifest.srcFile 'AndroidManifest-common.xml'
-            proto.srcDirs 'protos/'
+            proto {
+                srcDir 'protos/'
+                srcDir 'proto_overrides/'
+            }
         }
 
         androidTest {
-            java.srcDirs = ['tests/src']
             res.srcDirs = ['tests/res']
+            java.srcDirs = ['tests/src']
             manifest.srcFile "tests/AndroidManifest-common.xml"
         }
 
         aosp {
+            java.srcDirs = ['src_flags']
             manifest.srcFile "AndroidManifest.xml"
         }
 
         aospAndroidTest {
             manifest.srcFile "tests/AndroidManifest.xml"
         }
+
+        l3go {
+            res.srcDirs = ['go/res']
+            java.srcDirs = ['go/src_flags']
+            // Note: we are using the Launcher3 manifest here because the gradle manifest-merger uses
+            // different attributes than the build system.
+            manifest.srcFile "AndroidManifest.xml"
+        }
+
+        l3goAndroidTest {
+            manifest.srcFile "tests/AndroidManifest.xml"
+        }
     }
 }
 
@@ -68,12 +89,13 @@
 final String SUPPORT_LIBS_VERSION = '26.0.0-SNAPSHOT'
 dependencies {
     compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
+    compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
     compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
     compile "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
-    compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
+    compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
 
     testCompile 'junit:junit:4.12'
-    androidTestCompile "org.mockito:mockito-core:1.+"
+    androidTestCompile "org.mockito:mockito-core:1.9.5"
     androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
     androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
     androidTestCompile 'com.android.support.test:runner:0.5'
@@ -91,7 +113,9 @@
                 task.builtins {
                     remove java
                     javanano {
+                        option "java_package=launcher_log_extension.proto|com.android.launcher3.userevent.nano"
                         option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
+                        option "java_package=launcher_dump.proto|com.android.launcher3.model.nano"
                         option "enum_style=java"
                     }
                 }
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
new file mode 100644
index 0000000..fbaf981
--- /dev/null
+++ b/go/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.launcher3" >
+
+    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+
+    <application
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/ic_launcher_home"
+        android:label="@string/derived_app_name"
+        android:theme="@style/LauncherTheme"
+        android:largeHeap="@bool/config_largeHeap"
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
+        <!-- Activity for handling PinItemRequest. Only supports shortcuts -->
+        <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"
+            tools:node="replace" >
+            <intent-filter>
+                <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
diff --git a/go/res/drawable/ic_widget.xml b/go/res/drawable/ic_widget.xml
new file mode 100644
index 0000000..5336876
--- /dev/null
+++ b/go/res/drawable/ic_widget.xml
@@ -0,0 +1,25 @@
+<!--
+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
new file mode 100644
index 0000000..49506d9
--- /dev/null
+++ b/go/res/layout/widget_cell_content.xml
@@ -0,0 +1,66 @@
+<?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
new file mode 100644
index 0000000..10ac473
--- /dev/null
+++ b/go/res/values-af/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..b3b253f
--- /dev/null
+++ b/go/res/values-am/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2b3b807
--- /dev/null
+++ b/go/res/values-ar/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c4b8cb7
--- /dev/null
+++ b/go/res/values-az-rAZ/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c4b8cb7
--- /dev/null
+++ b/go/res/values-az/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..0da5699
--- /dev/null
+++ b/go/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-be/strings.xml
new file mode 100644
index 0000000..4189e35
--- /dev/null
+++ b/go/res/values-be/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..1b85992
--- /dev/null
+++ b/go/res/values-bg/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c56c925
--- /dev/null
+++ b/go/res/values-bn/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..7042468
--- /dev/null
+++ b/go/res/values-bs-rBA/strings.xml
@@ -0,0 +1,26 @@
+<?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">"Dvaput dodirnite 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
new file mode 100644
index 0000000..7042468
--- /dev/null
+++ b/go/res/values-bs/strings.xml
@@ -0,0 +1,26 @@
+<?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">"Dvaput dodirnite 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
new file mode 100644
index 0000000..3b5c3f7
--- /dev/null
+++ b/go/res/values-ca/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..e4018f2
--- /dev/null
+++ b/go/res/values-cs/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..821d36a
--- /dev/null
+++ b/go/res/values-da/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..43a1b3a
--- /dev/null
+++ b/go/res/values-de/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..ae59907
--- /dev/null
+++ b/go/res/values-el/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2ee2c26
--- /dev/null
+++ b/go/res/values-en-rAU/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2ee2c26
--- /dev/null
+++ b/go/res/values-en-rGB/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2ee2c26
--- /dev/null
+++ b/go/res/values-en-rIN/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..5212c03
--- /dev/null
+++ b/go/res/values-es-rUS/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..3ae4588
--- /dev/null
+++ b/go/res/values-es/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-et/strings.xml
new file mode 100644
index 0000000..2513e65
--- /dev/null
+++ b/go/res/values-et/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-eu/strings.xml
new file mode 100644
index 0000000..9949ef0
--- /dev/null
+++ b/go/res/values-eu/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..8bc5256
--- /dev/null
+++ b/go/res/values-fa/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..da9b0e1
--- /dev/null
+++ b/go/res/values-fi/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c7fd6d6
--- /dev/null
+++ b/go/res/values-fr-rCA/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..238fe73
--- /dev/null
+++ b/go/res/values-fr/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-gl/strings.xml
new file mode 100644
index 0000000..31621d5
--- /dev/null
+++ b/go/res/values-gl/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-gu/strings.xml
new file mode 100644
index 0000000..bdb549f
--- /dev/null
+++ b/go/res/values-gu/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..bc05776
--- /dev/null
+++ b/go/res/values-hi/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..fccdeb5
--- /dev/null
+++ b/go/res/values-hr/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..369c227
--- /dev/null
+++ b/go/res/values-hu/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-hy/strings.xml
new file mode 100644
index 0000000..4747f6d
--- /dev/null
+++ b/go/res/values-hy/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c8b9da3
--- /dev/null
+++ b/go/res/values-in/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-is/strings.xml
new file mode 100644
index 0000000..b8bb923
--- /dev/null
+++ b/go/res/values-is/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..bc5d998
--- /dev/null
+++ b/go/res/values-it/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..f541d4d
--- /dev/null
+++ b/go/res/values-iw/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..8f02d7f
--- /dev/null
+++ b/go/res/values-ja/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..1b46534
--- /dev/null
+++ b/go/res/values-ka-rGE/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..1b46534
--- /dev/null
+++ b/go/res/values-ka/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..e909818
--- /dev/null
+++ b/go/res/values-kk/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..40082a4
--- /dev/null
+++ b/go/res/values-km/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..9c121fd
--- /dev/null
+++ b/go/res/values-kn/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..60f925e
--- /dev/null
+++ b/go/res/values-ko/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..4c7e973
--- /dev/null
+++ b/go/res/values-ky-rKG/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..4c7e973
--- /dev/null
+++ b/go/res/values-ky/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..7864884
--- /dev/null
+++ b/go/res/values-lo-rLA/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..7864884
--- /dev/null
+++ b/go/res/values-lo/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..8f49032
--- /dev/null
+++ b/go/res/values-lt/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..04315eb
--- /dev/null
+++ b/go/res/values-lv/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-mk/strings.xml
new file mode 100644
index 0000000..52d66b5
--- /dev/null
+++ b/go/res/values-mk/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..b3c12e1
--- /dev/null
+++ b/go/res/values-ml/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c89dfd1
--- /dev/null
+++ b/go/res/values-mn/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2c767b4
--- /dev/null
+++ b/go/res/values-mr/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-ms/strings.xml
new file mode 100644
index 0000000..42add9a
--- /dev/null
+++ b/go/res/values-ms/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-my/strings.xml
new file mode 100644
index 0000000..5784df6
--- /dev/null
+++ b/go/res/values-my/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..2a5ffb6
--- /dev/null
+++ b/go/res/values-nb/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-ne/strings.xml
new file mode 100644
index 0000000..0be0375
--- /dev/null
+++ b/go/res/values-ne/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..5bcd016
--- /dev/null
+++ b/go/res/values-nl/strings.xml
@@ -0,0 +1,26 @@
+<?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-pa/strings.xml b/go/res/values-pa/strings.xml
new file mode 100644
index 0000000..f3982ab
--- /dev/null
+++ b/go/res/values-pa/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..45a1dc2
--- /dev/null
+++ b/go/res/values-pl/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..7a75a05
--- /dev/null
+++ b/go/res/values-pt-rPT/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..53bbfc4
--- /dev/null
+++ b/go/res/values-pt/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..75d1796
--- /dev/null
+++ b/go/res/values-ro/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..9c5c8cd
--- /dev/null
+++ b/go/res/values-ru/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..4b25c90
--- /dev/null
+++ b/go/res/values-si-rLK/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..4b25c90
--- /dev/null
+++ b/go/res/values-si/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..fc02933
--- /dev/null
+++ b/go/res/values-sk/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..6ecedfb
--- /dev/null
+++ b/go/res/values-sl/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-sq/strings.xml
new file mode 100644
index 0000000..bb74db6
--- /dev/null
+++ b/go/res/values-sq/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..0b9aea2
--- /dev/null
+++ b/go/res/values-sr/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..c3f434c
--- /dev/null
+++ b/go/res/values-sv/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..0379ed0
--- /dev/null
+++ b/go/res/values-sw/strings.xml
@@ -0,0 +1,26 @@
+<?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">"Gonga 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/strings.xml b/go/res/values-ta/strings.xml
new file mode 100644
index 0000000..50059b6
--- /dev/null
+++ b/go/res/values-ta/strings.xml
@@ -0,0 +1,26 @@
+<?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/strings.xml b/go/res/values-te/strings.xml
new file mode 100644
index 0000000..0bdf743
--- /dev/null
+++ b/go/res/values-te/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..e73d89f
--- /dev/null
+++ b/go/res/values-th/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..8f44ec5
--- /dev/null
+++ b/go/res/values-tl/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..f0f3cce
--- /dev/null
+++ b/go/res/values-tr/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..8d1f583
--- /dev/null
+++ b/go/res/values-uk/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..46bd823
--- /dev/null
+++ b/go/res/values-ur/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..318bc15
--- /dev/null
+++ b/go/res/values-uz-rUZ/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..318bc15
--- /dev/null
+++ b/go/res/values-uz/strings.xml
@@ -0,0 +1,26 @@
+<?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-v26/bools.xml b/go/res/values-v26/bools.xml
new file mode 100644
index 0000000..cc4a7ba
--- /dev/null
+++ b/go/res/values-v26/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <bool name="notification_badging_enabled">false</bool>
+</resources>
\ No newline at end of file
diff --git a/go/res/values-vi/strings.xml b/go/res/values-vi/strings.xml
new file mode 100644
index 0000000..1197619
--- /dev/null
+++ b/go/res/values-vi/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..57351d3
--- /dev/null
+++ b/go/res/values-zh-rCN/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..dea7749
--- /dev/null
+++ b/go/res/values-zh-rHK/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..07ae2ed
--- /dev/null
+++ b/go/res/values-zh-rTW/strings.xml
@@ -0,0 +1,26 @@
+<?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
new file mode 100644
index 0000000..e5051df
--- /dev/null
+++ b/go/res/values-zu/strings.xml
@@ -0,0 +1,26 @@
+<?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/res/drawable/bg_white_round_rect.xml b/go/res/values/dimens.xml
similarity index 75%
rename from res/drawable/bg_white_round_rect.xml
rename to go/res/values/dimens.xml
index c7f786f..f1b1053 100644
--- a/res/drawable/bg_white_round_rect.xml
+++ b/go/res/values/dimens.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="#FFFFFF" />
-    <corners android:radius="@dimen/bg_round_rect_radius" />
-</shape>
\ No newline at end of file
+<resources>
+    <!-- Dynamic Grid -->
+    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/res/values/override.xml b/go/res/values/override.xml
new file mode 100644
index 0000000..268cb98
--- /dev/null
+++ b/go/res/values/override.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.
+*/
+-->
+<resources>
+    <!-- String representing the intent to delete a package. -->
+    <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
+</resources>
\ No newline at end of file
diff --git a/go/res/values/strings.xml b/go/res/values/strings.xml
new file mode 100644
index 0000000..8ef2e62
--- /dev/null
+++ b/go/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?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
new file mode 100644
index 0000000..487c026
--- /dev/null
+++ b/go/res/xml/device_profiles.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+    <profile
+        launcher:name="Go Device"
+        launcher:minWidthDps="296"
+        launcher:minHeightDps="491.33"
+        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"
+        />
+
+</profiles>
\ No newline at end of file
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/go/src_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 61%
copy from src_config/com/android/launcher3/config/ProviderConfig.java
copy to go/src_flags/com/android/launcher3/config/FeatureFlags.java
index 491fa65..b11bb7c 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/go/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,14 @@
 
 package com.android.launcher3.config;
 
-public class ProviderConfig {
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ */
+public final class FeatureFlags extends BaseFlags {
 
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
+    private FeatureFlags() {}
 
-    public static final boolean IS_DOGFOOD_BUILD = true;
+    // Features to control Launcher3Go behavior
+    public static final boolean GO_DISABLE_WIDGETS = true;
+    public static final boolean LAUNCHER3_SPRING_ICONS = false;
 }
diff --git a/proguard.flags b/proguard.flags
index 8ee6ccd..51abcca 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -82,6 +82,10 @@
   *;
 }
 
+-keep class com.android.launcher3.graphics.ShadowDrawable {
+  public <init>(...);
+}
+
 # Proguard will strip methods required for talkback to properly scroll to
 # 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
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/proto_overrides/launcher_log_extension.proto
similarity index 62%
copy from src_config/com/android/launcher3/config/ProviderConfig.java
copy to proto_overrides/launcher_log_extension.proto
index 491fa65..2995aa2 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/proto_overrides/launcher_log_extension.proto
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,12 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+syntax = "proto2";
 
-package com.android.launcher3.config;
+option java_package = "com.android.launcher3.userevent";
+option java_outer_classname = "LauncherLogExtensions";
 
-public class ProviderConfig {
+package userevent;
 
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
+//
+// Use this to add any app specific extensions to the proto.
+//
+message LauncherEventExtension {
+}
 
-    public static final boolean IS_DOGFOOD_BUILD = true;
+message TargetExtension {
 }
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 93e09ae..0bbec18 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -15,6 +15,8 @@
  */
 syntax = "proto2";
 
+import "launcher_log_extension.proto";
+
 option java_package = "com.android.launcher3.userevent";
 option java_outer_classname = "LauncherLogProto";
 
@@ -52,6 +54,7 @@
   optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET
   optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
   optional int32 predictedRank = 15;
+  optional TargetExtension extension = 16;
 }
 
 // Used to define what type of item a Target would represent.
@@ -144,7 +147,6 @@
 //         Action (Touch) + Target + Target
 //
 message LauncherEvent {
-
   required Action action = 1;
 
   // List of targets that touch actions can be operated on.
@@ -157,4 +159,6 @@
 
   optional bool is_in_multi_window_mode = 7;
   optional bool is_in_landscape_mode = 8;
+
+  optional LauncherEventExtension extension = 9;
 }
diff --git a/res/anim/discovery_bounce.xml b/res/anim/discovery_bounce.xml
deleted file mode 100644
index 1f7d466..0000000
--- a/res/anim/discovery_bounce.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:ordering="sequentially"
-        android:startOffset="200">
-
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="250"
-        android:interpolator="@interpolator/disco_bounce_section1"
-        android:valueFrom="1f"
-        android:valueTo=".94f"
-        android:valueType="floatType"/>
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="216"
-        android:interpolator="@interpolator/disco_bounce_section2"
-        android:valueFrom=".94f"
-        android:valueTo="1.012f"
-        android:valueType="floatType"/>
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="234"
-        android:interpolator="@interpolator/disco_bounce_section3"
-        android:valueFrom="1.012f"
-        android:valueTo="1f"
-        android:valueType="floatType"/>
-</set>
diff --git a/res/animator-v23/discovery_bounce.xml b/res/animator-v23/discovery_bounce.xml
new file mode 100644
index 0000000..8d0e8fd
--- /dev/null
+++ b/res/animator-v23/discovery_bounce.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="2166"
+    android:repeatCount="5">
+    <propertyValuesHolder
+        android:propertyName="progress"
+        android:valueType="floatType">
+        <keyframe
+            android:fraction="0"
+            android:value="1f" />
+        <keyframe
+            android:fraction="0.346"
+            android:value="1f" />
+        <keyframe
+            android:fraction=".423"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="0.9438f" />
+        <keyframe
+            android:fraction="0.654"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="1f" />
+        <keyframe
+            android:fraction="1"
+            android:value="1f" />
+    </propertyValuesHolder>
+</objectAnimator>
diff --git a/res/animator/discovery_bounce.xml b/res/animator/discovery_bounce.xml
new file mode 100644
index 0000000..f02ebdb
--- /dev/null
+++ b/res/animator/discovery_bounce.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:ordering="sequentially">
+    <objectAnimator
+        android:duration="166"
+        android:interpolator="@interpolator/disco_bounce"
+        android:propertyName="progress"
+        android:startOffset="750"
+        android:valueFrom="1f"
+        android:valueTo="0.9438f"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:duration="500"
+        android:interpolator="@interpolator/disco_bounce"
+        android:propertyName="progress"
+        android:valueTo="1f"
+        android:valueType="floatType" />
+</set>
diff --git a/res/color-v24/all_apps_bg_hand_fill.xml b/res/color-v24/all_apps_bg_hand_fill.xml
new file mode 100644
index 0000000..1b0b538
--- /dev/null
+++ b/res/color-v24/all_apps_bg_hand_fill.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:startX="158.5"
+    android:startY="141.5"
+    android:endX="196.0"
+    android:endY="206.5"
+    android:type="linear" >
+    <item android:offset="0" android:color="#E1E1E1" />
+    <item android:offset="0.3317" android:color="#E1E1E1" />
+    <item android:offset="0.493" android:color="#C1E5E5E5" />
+    <item android:offset="1" android:color="#00EEEEEE" />
+</gradient>
\ No newline at end of file
diff --git a/res/color-v24/all_apps_bg_hand_fill_dark.xml b/res/color-v24/all_apps_bg_hand_fill_dark.xml
new file mode 100644
index 0000000..bb9c71c
--- /dev/null
+++ b/res/color-v24/all_apps_bg_hand_fill_dark.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<gradient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:startX="158.5"
+    android:startY="141.5"
+    android:endX="196.0"
+    android:endY="206.5"
+    android:type="linear" >
+    <item android:offset="0" android:color="#9AA0A6" />
+    <item android:offset="0.1013" android:color="#E5A5ABB0" />
+    <item android:offset="0.4946" android:color="#81CDCFD1" />
+    <item android:offset="0.8079" android:color="#31E5E6E6" />
+    <item android:offset="1" android:color="#00EEEEEE" />
+</gradient>
\ No newline at end of file
diff --git a/res/drawable-hdpi/ic_all_apps_bg_hand.png b/res/drawable-hdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 437fd37..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_info_launcher.png b/res/drawable-hdpi/ic_info_launcher.png
deleted file mode 100644
index 11162e1..0000000
--- a/res/drawable-hdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove_launcher.png b/res/drawable-hdpi/ic_remove_launcher.png
deleted file mode 100644
index ad2b9af..0000000
--- a/res/drawable-hdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_uninstall_launcher.png b/res/drawable-hdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 426683c..0000000
--- a/res/drawable-hdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
deleted file mode 100644
index 2bbf5cd..0000000
--- a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
+++ /dev/null
@@ -1,27 +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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorAccent" />
-    <size
-        android:width="64dp"
-        android:height="64dp" />
-    <corners
-        android:topLeftRadius="64dp"
-        android:topRightRadius="64dp"
-        android:bottomRightRadius="64dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable-mdpi/ic_all_apps_bg_hand.png b/res/drawable-mdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 0a00241..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_info_launcher.png b/res/drawable-mdpi/ic_info_launcher.png
deleted file mode 100644
index 6fbe5e3..0000000
--- a/res/drawable-mdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove_launcher.png b/res/drawable-mdpi/ic_remove_launcher.png
deleted file mode 100644
index 2bb281d..0000000
--- a/res/drawable-mdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_uninstall_launcher.png b/res/drawable-mdpi/ic_uninstall_launcher.png
deleted file mode 100644
index bfcbc6df..0000000
--- a/res/drawable-mdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_white_round_rect.xml b/res/drawable-v24/ic_info_shadow.xml
similarity index 69%
copy from res/drawable/bg_white_round_rect.xml
copy to res/drawable-v24/ic_info_shadow.xml
index c7f786f..1fe2c46 100644
--- a/res/drawable/bg_white_round_rect.xml
+++ b/res/drawable-v24/ic_info_shadow.xml
@@ -5,7 +5,7 @@
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+        http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,9 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="#FFFFFF" />
-    <corners android:radius="@dimen/bg_round_rect_radius" />
-</shape>
\ No newline at end of file
+<com.android.launcher3.graphics.ShadowDrawable
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_info_no_shadow"
+    android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/layout/qsb_blocker_view.xml b/res/drawable-v24/ic_remove_shadow.xml
similarity index 69%
copy from res/layout/qsb_blocker_view.xml
copy to res/drawable-v24/ic_remove_shadow.xml
index 453eebe..48abc10 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/drawable-v24/ic_remove_shadow.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+        http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.qsb.QsbBlockerView
+<com.android.launcher3.graphics.ShadowDrawable
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:src="@drawable/ic_remove_no_shadow"
+    android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/layout/qsb_blocker_view.xml b/res/drawable-v24/ic_uninstall_shadow.xml
similarity index 69%
copy from res/layout/qsb_blocker_view.xml
copy to res/drawable-v24/ic_uninstall_shadow.xml
index 453eebe..b441b0e 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/drawable-v24/ic_uninstall_shadow.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+        http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.qsb.QsbBlockerView
+<com.android.launcher3.graphics.ShadowDrawable
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:src="@drawable/ic_uninstall_no_shadow"
+    android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_hand.png b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 1acb378..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_info_launcher.png b/res/drawable-xhdpi/ic_info_launcher.png
deleted file mode 100644
index 041f2b3..0000000
--- a/res/drawable-xhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove_launcher.png b/res/drawable-xhdpi/ic_remove_launcher.png
deleted file mode 100644
index ff94eb8..0000000
--- a/res/drawable-xhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_uninstall_launcher.png b/res/drawable-xhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 2c7ab56..0000000
--- a/res/drawable-xhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 09c6c8d..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_info_launcher.png b/res/drawable-xxhdpi/ic_info_launcher.png
deleted file mode 100644
index 8e602da..0000000
--- a/res/drawable-xxhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove_launcher.png b/res/drawable-xxhdpi/ic_remove_launcher.png
deleted file mode 100644
index 78ca080..0000000
--- a/res/drawable-xxhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_uninstall_launcher.png b/res/drawable-xxhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 43aba6e..0000000
--- a/res/drawable-xxhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 49c004d..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_info_launcher.png b/res/drawable-xxxhdpi/ic_info_launcher.png
deleted file mode 100644
index 3540de1..0000000
--- a/res/drawable-xxxhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_remove_launcher.png b/res/drawable-xxxhdpi/ic_remove_launcher.png
deleted file mode 100644
index 418d81a..0000000
--- a/res/drawable-xxxhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_uninstall_launcher.png b/res/drawable-xxxhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 724437a..0000000
--- a/res/drawable-xxxhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/container_fastscroll_popup_bg.xml b/res/drawable/container_fastscroll_popup_bg.xml
deleted file mode 100644
index 3dc7680..0000000
--- a/res/drawable/container_fastscroll_popup_bg.xml
+++ /dev/null
@@ -1,27 +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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorAccent" />
-    <size
-        android:width="64dp"
-        android:height="64dp" />
-    <corners
-        android:topLeftRadius="64dp"
-        android:topRightRadius="64dp"
-        android:bottomLeftRadius="64dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/deep_shortcuts_drag_handle.xml
index 82e844d..8fc3779 100644
--- a/res/drawable/deep_shortcuts_drag_handle.xml
+++ b/res/drawable/deep_shortcuts_drag_handle.xml
@@ -22,6 +22,7 @@
         android:tint="?android:attr/textColorHint" >
 
     <path
-        android:pathData="M20 9H4v2h16V9zM4 15h16v-2H4v2z"
+        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/gutter_horizontal.xml b/res/drawable/gutter_horizontal.xml
new file mode 100644
index 0000000..95b03df
--- /dev/null
+++ b/res/drawable/gutter_horizontal.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This "gutter" has a shadow at the top and a subtler shadow on the bottom. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient android:type="linear"
+              android:angle="-90"
+              android:startColor="#ffE0E0E0"
+              android:centerColor="#ffffffff"
+              android:endColor="#ffF5F5F5" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_hand.xml b/res/drawable/ic_all_apps_bg_hand.xml
new file mode 100644
index 0000000..7f3fe14
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_hand.xml
@@ -0,0 +1,100 @@
+<?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="208dp"
+    android:height="212dp"
+    android:viewportWidth="208.0"
+    android:viewportHeight="212.0">
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M89.4,61.8H85l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C92.2,16.6,76,0.3,55.9,0.3
+        S19.5,16.6,19.5,36.6S35.8,73,55.9,73c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L89.4,61.8z M54,66.6
+        c-13.9,0-28.8-16-28.8-30S41.5,8.9,55.4,8.9S81,22.7,81,36.6S67.9,66.6,54,66.6z"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M33.4,29.2l-0.3-1.8l-4.2-3.1L18.1,26l-3.1,4.2l0.3,1.8L4.5,33.7L9,62.5
+        c0.2,1.5,1.6,2.5,3.1,2.3l34.2-5.3c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.8L33.4,29.2z"/>
+    <path
+        android:fillColor="#3367D6"
+        android:pathData="M30.2,27.9l-0.3-1.8l-4.1-3L15,24.7l-3,4.1l0.3,1.8L1.6,32.3L6,60.9
+        c0.2,1.5,1.6,2.5,3.1,2.3L43,57.9c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.6L30.2,27.9z M26.6,28.4l-10.7,1.6l-0.3-1.8l10.7-1.6L26.6,28.4z"/>
+    <group>
+        <clip-path
+            android:pathData="M25.1,37.7a28.9,28.9 0 1,0 57.8,0a28.9,28.9 0 1,0 -57.8,0"/>
+        <path
+            android:fillColor="#4285F4"
+            android:pathData="M41.7,23l-0.3-2.1l-4.9-3.6l-12.6,1.9l-3.6,4.9l0.3,2.1L8.1,28.1l5.2,33.7
+                c0.3,1.7,1.9,2.9,3.6,2.7l40-6.1c1.7-0.3,2.9-1.9,2.7-3.6L54.4,21L41.7,23z M37.5,23.6l-12.6,1.9l-0.3-2.1l12.6-1.9L37.5,23.6z"/>
+    </group>
+    <path
+        android:fillColor="?android:attr/colorControlHighlight"
+        android:pathData="M87.5,62.9h-4.4l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C90.3,17.7,74,1.4,54,1.4
+        S17.6,17.7,17.6,37.7S33.9,74.1,54,74.1c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L87.5,62.9z M54,64.2
+        c-14.7,0-26.5-11.8-26.5-26.5S39.3,11.2,54,11.2s26.5,11.8,26.5,26.5S68.6,64.2,54,64.2z"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M153.4,112.9c-14.9-17.2-38.6-9.1-38.6-9.1l-10.2-11.3c0,0-4.8-5.9-9-3.7
+        c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l17.1,3.7l-0.9-0.7l-1-0.7L153.4,112.9z"/>
+    <path
+        android:fillColor="#FFDBA6"
+        android:pathData="M152.1,113.9c-14.9-17.2-37.6-8-37.6-8l-11.1-12.3c0,0-4.8-5.9-9-3.7
+        c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l19,4.1"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M148.6,77.9c0.6,0.7,2,2.5,2.1,2.6c1.1,1.7,6.2,13.6,11.8,35.1c0,0.1,1.9,3,1.9,3.1
+        c0,0,0.1,0.1,0.1,0.2c0,0,0,0,0-0.1c0.9,1.3,4.4,6.6,8.9,13.7c0.1,0.2,0.3,0.5,0.4,0.7c0,0.1,0.1,0.1,0.1,0.2
+        c0.2,0.3,0.4,0.6,0.6,0.9c0.1-0.1,0.2-0.2,0.3-0.3c0.2-0.3,0.6-0.3,0.8,0c2.9,4.8,21.2,35,26.7,49c2.1,5.3,3.2,8.4,3.6,11.6
+        c0.3,2.3,0,4.4-1.2,6c1.5-1.9,3.5-6.8-1.5-19c-1.2-2.9-2.8-6.5-4.8-10.5c-7.5-15.2-20-35.6-22.4-39.6c-0.2-0.3-0.6-0.3-0.8,0
+        c-0.2,0.2-0.3,0.4-0.5,0.6c-4.5-7.1-8.2-12.6-8.8-13.5c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.6-10.7-33.4-11.8-35.1
+        c-0.1-0.1-1.5-1.9-2.1-2.6l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3c-2.5,1.8-1.6,4.5-0.1,7.1l3.3,5.2l7-2
+        C147.7,77.4,148.1,77.3,148.6,77.9z"/>
+    <path
+        android:fillColor="#FFDBA6"
+        android:pathData="M148.6,77.9l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l3.8,6L148.6,77.9C148.6,77.9,148.6,77.9,148.6,77.9z"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M151.1,92.5l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L151.1,92.5z"/>
+    <path
+        android:fillColor="#FFDBA6"
+        android:pathData="M149.7,92.9l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L149.7,92.9z"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M141.6,94.6l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0l0,0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L141.6,94.6z"/>
+    <path
+        android:fillColor="#FFDBA6"
+        android:pathData="M140.1,95l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0h0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L140.1,95z"/>
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M140.4,99.1c-0.5-0.6-2.1-7.5-2.8-7.3l-15.9-6.9c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5
+        l-0.4-0.6L100,54.5c-1.5-2-3.3-4-5.3-4.3c-0.7-0.1-1.3-0.2-2,0.2v0c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L108.5,87
+        l0.2,0.4l6.6,11.7l0,0c2.5,4.5,4.4,10.5,4.4,10.7L140.4,99.1"/>
+    <path
+        android:fillColor="#FFDBA6"
+        android:pathData="M129.7,125.1c3,0.7,8.1,4,11.8,9.1c2.7,3.7,5.5,8.3,8.2,13
+        c7.6-2.3,19.9-6.8,24.9-12.9c-0.2-0.3-0.4-0.6-0.6-0.9c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.2-0.3-0.5-0.4-0.7c-4.5-7.1-8-12.4-8.9-13.7
+        c0,0,0,0,0,0.1c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.5-10.7-33.4-11.8-35.1c-0.1-0.1-1.5-1.9-2.1-2.6
+        c-0.5-0.6-0.9-0.5-1.6-0.3l-26.8,7.7c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5l-0.4-0.6L98.5,54.8c-1.5-2-3.3-4-5.3-4.3
+        c-0.7-0.1-1.3-0.2-2,0.2c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L107,87.3l0.2,0.4l6.6,11.7l0,0
+        c2.5,4.5,4.4,10.5,4.4,10.7L129.7,125.1"/>
+    <path
+        android:fillColor="?android:attr/colorForeground"
+        android:pathData="M202.3,183.1c-5.4-14.1-23.8-44.3-26.7-49c-0.2-0.3-0.6-0.3-0.8,0
+        c-5.1,6.6-19,11.4-26.5,13.6c-0.3,0.1-1,0.1-1.2,0.6c-0.2,0.4,0.1,1,0.2,1.1c7.8,12.9,14.7,27.9,15.3,29.3c0,0.1,0.1,0.1,0.1,0.2
+        l9.6,22.9c0,0,0,0,0,0l1.7,4.1c1.4,2.7,3,4.3,5.3,5.1c1.5,0.5,2.1,0.6,3.2,0.6c4.8,0.1,15.2-6.1,20.5-9.4c2.7-1.7,3.3-4.4,2.9-7.6
+        C205.5,191.5,204.4,188.4,202.3,183.1z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_1.xml b/res/drawable/ic_all_apps_bg_icon_1.xml
index c9c0a75..d226ac6 100644
--- a/res/drawable/ic_all_apps_bg_icon_1.xml
+++ b/res/drawable/ic_all_apps_bg_icon_1.xml
@@ -25,7 +25,7 @@
         android:pathData="M44.28,30.96c4.84-10.68,0.09-23.27-10.59-28.11S10.42,2.74,5.58,13.42
         C1,23.54,6.5,35.92,16.62,40.51l0,0l-3.23,7.12C27.84,47,39.79,40.86,44.28,30.96z" />
     <path
-        android:fillColor="#E0E0E0"
+        android:fillColor="?android:attr/colorPrimary"
         android:pathData="M41.75,30.05c4.84-10.68,0.09-23.27-10.59-28.11S7.9,1.83,3.06,12.51
         c-4.59,10.12,0.92,22.5,11.03,27.09l0,0l-3.23,7.12C25.31,46.09,37.26,39.94,41.75,30.05z" />
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_2.xml b/res/drawable/ic_all_apps_bg_icon_2.xml
index b6269e3..5966d99 100644
--- a/res/drawable/ic_all_apps_bg_icon_2.xml
+++ b/res/drawable/ic_all_apps_bg_icon_2.xml
@@ -26,7 +26,7 @@
         c-0.25-0.58-0.9-0.99-1.89-1.1L6.2,5.99C5.39,5.91,4.74,6.08,4.32,6.44l0,0C3.7,6.97,3.55,7.88,4.01,8.96l14.54,34.09
         C19,44.13,19.75,44.65,20.54,44.59L20.54,44.59z" />
     <path
-        android:fillColor="#E0E0E0"
+        android:fillColor="?android:attr/colorPrimary"
         android:pathData="M18.49,43.22c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
         c-0.25-0.58-0.9-0.99-1.89-1.1L4.15,4.62C3.34,4.54,2.69,4.71,2.27,5.08l0,0C1.65,5.6,1.5,6.52,1.96,7.6L16.5,41.69
         C16.96,42.76,17.7,43.28,18.49,43.22L18.49,43.22z" />
diff --git a/res/drawable/ic_all_apps_bg_icon_3.xml b/res/drawable/ic_all_apps_bg_icon_3.xml
index 4c255a9..b18f8bc 100644
--- a/res/drawable/ic_all_apps_bg_icon_3.xml
+++ b/res/drawable/ic_all_apps_bg_icon_3.xml
@@ -26,11 +26,11 @@
         s22.31-9.99,22.31-22.31S37.5,1.27,25.18,1.27z M25.18,33.55c-5.5,0-14.35-5.1-14.35-10.6s8.32-12.19,13.82-12.19
         c5.5,0,10.49,7.33,10.49,12.83S30.68,33.55,25.18,33.55z" />
     <path
-        android:fillColor="#E0E0E0"
+        android:fillColor="?android:attr/colorPrimary"
         android:pathData="M22.93,0.22c-12.32,0-22.31,9.99-22.31,22.31s9.99,22.31,22.31,22.31
         s22.31-9.99,22.31-22.31S35.25,0.22,22.93,0.22z M22.93,32.5c-5.5,0-9.97-4.46-9.97-9.97s4.46-9.97,9.97-9.97
         c5.5,0,9.97,4.46,9.97,9.97S28.43,32.5,22.93,32.5z" />
     <path
-        android:fillColor="#E0E0E0"
+        android:fillColor="?android:attr/colorPrimary"
         android:pathData="M14.81,22.53a8.12,8.12 0 1,0 16.24,0a8.12,8.12 0 1,0 -16.24,0z" />
 </vector>
diff --git a/res/drawable/ic_all_apps_bg_icon_4.xml b/res/drawable/ic_all_apps_bg_icon_4.xml
index 12e05bc..8eb4d90 100644
--- a/res/drawable/ic_all_apps_bg_icon_4.xml
+++ b/res/drawable/ic_all_apps_bg_icon_4.xml
@@ -25,7 +25,7 @@
         android:pathData="M11.53,8.02l23.39-5.73c1.61-0.39,3.25,0.6,3.64,2.21l7.64,31.19
         c0.39,1.61-0.6,3.25-2.21,3.64L12.8,46.97c-1.61,0.39-3.25-0.6-3.64-2.21L3.43,21.37L11.53,8.02z" />
     <path
-        android:fillColor="#E0E0E0"
+        android:fillColor="?android:attr/colorPrimary"
         android:pathData="M9.2,6.53L32.59,0.8C34.2,0.4,35.84,1.4,36.23,3l7.64,31.19c0.39,1.61-0.6,3.25-2.21,3.64
         l-31.19,7.64c-1.61,0.39-3.25-0.6-3.64-2.21L1.11,19.87L9.2,6.53z" />
     <path
diff --git a/res/drawable/ic_info_no_shadow.xml b/res/drawable/ic_info_no_shadow.xml
index 3d0c6d6..b5512c3 100644
--- a/res/drawable/ic_info_no_shadow.xml
+++ b/res/drawable/ic_info_no_shadow.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/textColorPrimary" >
     <path
-        android:fillColor="@color/workspace_icon_text_color"
-        android:pathData="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"/>
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,17L12,17c0.55,0,1-0.45,1-1v-4c0-0.55-0.45-1-1-1l0,0c-0.55,0-1,0.45-1,1v4C11,16.55,11.45,17,12,17z M12,2C6.48,
+        2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S16.41,20,12,20zM12,
+        9.1L12,9.1c0.61,0,1.1-0.49,1.1-1.1l0,0c0-0.61-0.49-1.1-1.1-1.1l0,0c-0.61,0-1.1,0.49-1.1,1.1l0,0C10.9,8.61,11.39,9.1,12,9.1z"/>
 </vector>
diff --git a/res/drawable/ic_instant_app_badge.xml b/res/drawable/ic_instant_app_badge.xml
new file mode 100644
index 0000000..cc53230
--- /dev/null
+++ b/res/drawable/ic_instant_app_badge.xml
@@ -0,0 +1,43 @@
+<?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_remove_no_shadow.xml b/res/drawable/ic_remove_no_shadow.xml
new file mode 100644
index 0000000..be7f9f3
--- /dev/null
+++ b/res/drawable/ic_remove_no_shadow.xml
@@ -0,0 +1,27 @@
+<?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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="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"/>
+</vector>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index e89c158..1bab189 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -19,6 +19,12 @@
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="@color/workspace_icon_text_color"
-        android:pathData="M38.86 25.95c.08-.64 .14-1.29 .14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3 .49-.84 .24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91 .37-.99 .84l-.75 5.3c-1.22 .51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22 .43l-4 6.93c-.25 .43-.14 .97 .24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31 .14 1.95l-4.22 3.31c-.38 .3-.49 .84-.24 1.28l4 6.93c.25 .43 .77 .61 1.22 .43l4.98-2.01c1.03 .79 2.16 1.46 3.38 1.97l.75 5.3c.08 .47 .49 .84 .99 .84h8c.5 0 .91-.37 .99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45 .17 .97 0 1.22-.43l4-6.93c.25-.43 .14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
+        android:fillColor="?android:attr/textColorPrimary"
+        android:pathData="M42.8,28.4l-3.88-2.9c0.06-0.5,0.08-1,0.08-1.52s-0.02-1.02-0.08-1.52l3.88-2.86c0.84-0.62,1.04-1.88,
+        0.48-2.82l-3.2-5.52c-0.56-0.96-1.76-1.4-2.72-1l-4.28,1.82c-0.96-0.74-2.02-1.36-3.14-1.84l-0.54-4.4C29.28,4.8,28.28,4,
+        27.18,4h-6.36c-1.1,0-2.1,0.8-2.22,1.84l-0.52,4.38c-1.14,0.48-2.2,1.1-3.16,1.84l-4.28-1.82c-0.96-0.4-2.16,0.04-2.72,
+        1l-3.2,5.52c-0.56,0.96-0.36,2.2,0.48,2.84l3.88,2.9C9.02,22.98,9,23.48,9,24s0.02,1.02,0.08,1.52L5.2,28.4c-0.84,0.62-1.04,
+        1.88-0.48,2.82l3.2,5.52c0.56,0.96,1.76,1.4,2.72,1l4.28-1.82c0.96,0.74,2.02,1.36,3.14,1.84l0.54,4.38C18.72,43.2,19.72,
+        44,20.82,44h6.36c1.1,0,2.08-0.8,2.22-1.84l0.54-4.38c1.12-0.48,2.18-1.1,3.14-1.84l4.28,1.82c0.96,0.4,2.16-0.04,
+        2.72-1l3.2-5.52C43.84,30.28,43.64,29.04,42.8,28.4z M24,31c-3.86,0-7-3.14-7-7s3.14-7,7-7s7,3.14,7,7S27.86,31,24,31z"/>
 </vector>
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
new file mode 100644
index 0000000..2a86e10
--- /dev/null
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -0,0 +1,26 @@
+<?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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary" >
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,
+        3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4L18,4z"/>
+</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index b7fcfbf..9e9222f 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -19,6 +19,11 @@
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="@color/workspace_icon_text_color"
-        android:pathData="M8 8h14V4H8C5.79 4 4 5.79 4 8v14h4V8zm12 18l-8 10h24l-6-8-4.06 5.42L20 26zm14-9c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm6-13H26v4h14v14h4V8c0-2.21-1.79-4-4-4zm0 36H26v4h14c2.21 0 4-1.79 4-4V26h-4v14zM8 26H4v14c0 2.21 1.79 4 4 4h14v-4H8V26z"/>
+        android:fillColor="?android:attr/textColorPrimary"
+        android:pathData="M8,8h13c0.56,0,1-0.44,1-1V5c0-0.56-0.44-1-1-1H8C5.8,4,4,5.8,4,8v13c0,0.56,0.44,1,1,1h2c0.56,0,1-0.44,
+        1-1V8zM18.44,27.96L12,36h24l-4.4-5.86c-0.8-1.06-2.4-1.06-3.2,0l-2.46,3.28l-4.38-5.46C20.76,26.96,19.24,26.96,18.44,27.96z M34,
+        17c0-1.66-1.34-3-3-3s-3,1.34-3,3s1.34,3,3,3S34,18.66,34,17z M40,4H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13v13c0,
+        0.56,0.44,1,1,1h2c0.56,0,1-0.44,1-1V8C44,5.8,42.2,4,40,4z M40,40H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13c2.2,
+        0,4-1.8,4-4V27c0-0.56-0.44-1-1-1h-2c-0.56,0-1,0.44-1,1V40z M7,26H5c-0.56,0-1,0.44-1,1v13c0,2.2,1.8,4,4,4h13c0.56,0,1-0.44,
+        1-1v-2c0-0.56-0.44-1-1-1H8V27C8,26.44,7.56,26,7,26z"/>
 </vector>
diff --git a/res/drawable/ic_warning.xml b/res/drawable/ic_warning.xml
new file mode 100644
index 0000000..332563d
--- /dev/null
+++ b/res/drawable/ic_warning.xml
@@ -0,0 +1,27 @@
+<?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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        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"/>
+</vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 97706e3..de2980f 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -19,6 +19,8 @@
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="@color/workspace_icon_text_color"
-        android:pathData="M26 26v16h16V26H26zM6 42h16V26H6v16zM6 6v16h16V6H6zm27.31-2.63L22 14.69 33.31 26l11.31-11.31L33.31 3.37z"/>
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M26,28v12c0,1,0.8,2,2,2h12c1,0,2-1,2-2V28c0-1.2-1-2-2-2H28C26.8,26,26,26.8,26,28z M8,42h12c1.2,0,2-1,2-2V28
+        c0-1.2-0.8-2-2-2H8c-1,0-2,0.8-2,2v12C6,41,7,42,8,42z M6,8v12c0,1.2,1,2,2,2h12c1.2,0,2-0.8,2-2V8c0-1-0.8-2-2-2H8C7,6,6,7,6,8z
+        M32.6,4.6l-8,8c-0.8,0.8-0.8,2,0,2.8l8,8c0.8,0.8,2,0.8,2.8,0l8-8c0.8-0.8,0.8-2,0-2.8l-8-8C34.6,3.8,33.4,3.8,32.6,4.6z"/>
 </vector>
diff --git a/res/interpolator/disco_bounce_section1.xml b/res/interpolator/disco_bounce.xml
similarity index 81%
rename from res/interpolator/disco_bounce_section1.xml
rename to res/interpolator/disco_bounce.xml
index 2156216..6ec339e 100644
--- a/res/interpolator/disco_bounce_section1.xml
+++ b/res/interpolator/disco_bounce.xml
@@ -16,7 +16,7 @@
   -->
 
 <pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.9"
-                  android:controlY1="0"
-                  android:controlX2="0.5"
-                  android:controlY2="1"/>
+    android:controlX1="0.35"
+    android:controlY1="0"
+    android:controlX2="0.5"
+    android:controlY2="1" />
diff --git a/res/interpolator/disco_bounce_section2.xml b/res/interpolator/disco_bounce_section2.xml
deleted file mode 100644
index 86cc789..0000000
--- a/res/interpolator/disco_bounce_section2.xml
+++ /dev/null
@@ -1,22 +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
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.9"
-                  android:controlY1="0"
-                  android:controlX2="0.6"
-                  android:controlY2="1"/>
diff --git a/res/interpolator/disco_bounce_section3.xml b/res/interpolator/disco_bounce_section3.xml
deleted file mode 100644
index 1acef03..0000000
--- a/res/interpolator/disco_bounce_section3.xml
+++ /dev/null
@@ -1,22 +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
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.1"
-                  android:controlY1="0"
-                  android:controlX2="0.7"
-                  android:controlY2="1"/>
diff --git a/res/interpolator/folder_interpolator.xml b/res/interpolator/folder_interpolator.xml
new file mode 100644
index 0000000..b95d454
--- /dev/null
+++ b/res/interpolator/folder_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.2"
+    android:controlY1="0"
+    android:controlX2="0"
+    android:controlY2="1"/>
diff --git a/res/interpolator/large_folder_preview_item_close_interpolator.xml b/res/interpolator/large_folder_preview_item_close_interpolator.xml
new file mode 100644
index 0000000..d28af63
--- /dev/null
+++ b/res/interpolator/large_folder_preview_item_close_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.3"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1"/>
diff --git a/res/interpolator/large_folder_preview_item_open_interpolator.xml b/res/interpolator/large_folder_preview_item_open_interpolator.xml
new file mode 100644
index 0000000..b5661ab
--- /dev/null
+++ b/res/interpolator/large_folder_preview_item_open_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0"
+    android:controlY1="1"
+    android:controlX2="0.5"
+    android:controlY2="1"/>
diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..6a68f84
--- /dev/null
+++ b/res/layout-land/all_apps_fast_scroller.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.
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginTop="-5dp"
+        android:layout_marginEnd="-45dp" />
+
+    <com.android.launcher3.allapps.LandscapeFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="48dp"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="@+id/apps_list_view"
+        android:layout_marginEnd="-88dp"
+        android:layout_marginTop="14dp"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index ef0dfdc..ac440fc 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -27,7 +27,7 @@
         android:id="@+id/drag_layer"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:background="@drawable/workspace_bg"
+        android:background="?attr/workspaceStatusBarScrim"
         android:importantForAccessibility="no"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
@@ -35,12 +35,15 @@
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
+            android:theme="@style/HomeScreenElementTheme"
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center"
             launcher:pageIndicator="@id/page_indicator" />
 
+        <include layout="@layout/gradient_bg" />
+
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
@@ -57,18 +60,6 @@
             android:id="@+id/overview_panel"
             android:visibility="gone" />
 
-        <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
-            android:id="@+id/page_indicator"
-            android:layout_width="@dimen/dynamic_grid_page_indicator_height"
-            android:layout_height="@dimen/dynamic_grid_page_indicator_height"
-            android:layout_gravity="bottom|left"/>
-
-        <!-- A place holder view instead of the QSB in transposed layout -->
-        <View
-            android:layout_width="0dp"
-            android:layout_height="10dp"
-            android:id="@+id/workspace_blocked_row" />
-
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
             android:layout_width="match_parent"
@@ -81,6 +72,13 @@
             android:layout_height="match_parent"
             android:visibility="invisible" />
 
+        <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
+            android:id="@+id/page_indicator"
+            android:theme="@style/HomeScreenElementTheme"
+            android:layout_width="@dimen/dynamic_grid_min_page_indicator_size"
+            android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+            android:layout_gravity="bottom|left"/>
+
     </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index dd981dd..c41a6e3 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -29,13 +29,14 @@
         android:clipChildren="false"
         android:importantForAccessibility="no"
         android:clipToPadding="false"
-        android:background="@drawable/workspace_bg"
+        android:background="?attr/workspaceStatusBarScrim"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
+            android:theme="@style/HomeScreenElementTheme"
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -43,6 +44,8 @@
             launcher:pageIndicator="@+id/page_indicator">
         </com.android.launcher3.Workspace>
 
+        <include layout="@layout/gradient_bg" />
+
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
@@ -63,10 +66,6 @@
             android:id="@+id/drop_target_bar"
             layout="@layout/drop_target_bar_horz" />
 
-        <include
-            layout="@layout/qsb_container"
-            android:id="@+id/qsb_container" />
-
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
             android:layout_width="match_parent"
diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout-sw720dp/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?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"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+    <com.android.launcher3.views.RecyclerViewFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="@dimen/fastscroll_width"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_end_margin"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 06cb550..03e42bc 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -28,13 +28,14 @@
         android:clipChildren="false"
         android:clipToPadding="false"
         android:importantForAccessibility="no"
-        android:background="@drawable/workspace_bg"
+        android:background="?attr/workspaceStatusBarScrim"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
+            android:theme="@style/HomeScreenElementTheme"
             android:layout_gravity="center"
             android:id="@+id/workspace"
             android:layout_width="match_parent"
@@ -42,6 +43,8 @@
             launcher:pageIndicator="@id/page_indicator">
         </com.android.launcher3.Workspace>
 
+        <include layout="@layout/gradient_bg" />
+
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
@@ -62,10 +65,6 @@
         <include layout="@layout/page_indicator"
                  android:id="@+id/page_indicator" />
 
-        <include
-            layout="@layout/qsb_container"
-            android:id="@+id/qsb_container" />
-
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
             android:layout_width="match_parent"
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 35bcac3..6c316e6 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -17,57 +17,73 @@
 */
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <TextView
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp"
-        android:paddingTop="4dp"
-        android:paddingBottom="20dp"
-        android:text="@string/add_item_request_drag_hint"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <ScrollView
         android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:clipToPadding="false">
 
-    <FrameLayout
-        android:theme="@style/WidgetContainerTheme"
-        android:layout_width="match_parent"
-        android:background="?android:attr/colorPrimaryDark"
-        android:layout_height="wrap_content">
-
-        <com.android.launcher3.dragndrop.LivePreviewWidgetCell
-            android:layout_width="wrap_content"
+        <LinearLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:orientation="vertical"
-            android:focusable="true"
-            android:background="?android:attr/colorPrimaryDark"
-            android:gravity="center_horizontal"
-            android:id="@+id/widget_cell"
-            android:layout_gravity="center_horizontal" >
+            android:orientation="vertical">
 
-            <include layout="@layout/widget_cell_content"  />
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingBottom="20dp"
+                android:paddingLeft="24dp"
+                android:paddingRight="24dp"
+                android:paddingTop="4dp"
+                android:text="@string/add_item_request_drag_hint" />
 
-        </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
-    </FrameLayout>
+            <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="?android:attr/colorPrimaryDark"
+                android:theme="@style/WidgetContainerTheme">
+
+                <com.android.launcher3.dragndrop.LivePreviewWidgetCell
+                    android:id="@+id/widget_cell"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_weight="1"
+                    android:background="?android:attr/colorPrimaryDark"
+                    android:focusable="true"
+                    android:gravity="center_horizontal"
+                    android:orientation="vertical" >
+
+                    <include layout="@layout/widget_cell_content"  />
+
+                </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
+            </FrameLayout>
+        </LinearLayout>
+    </ScrollView>
 
     <LinearLayout
+        style="?android:attr/buttonBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        style="?android:attr/buttonBarStyle"
-        android:gravity="end" >
+        android:gravity="end"
+        android:paddingBottom="4dp"
+        android:paddingEnd="12dp"
+        android:paddingStart="12dp"
+        android:paddingTop="4dp" >
         <Button
+            style="?android:attr/buttonBarButtonStyle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@android:string/cancel"
             android:onClick="onCancelClick"
-            style="?android:attr/buttonBarButtonStyle" />
+            android:text="@android:string/cancel" />
         <Button
+            style="?android:attr/buttonBarButtonStyle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/place_automatically"
             android:onClick="onPlaceAutomaticallyClick"
-            style="?android:attr/buttonBarButtonStyle" />
+            android:text="@string/place_automatically" />
     </LinearLayout>
 </LinearLayout>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index d6bdac2..39df2b1 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -16,7 +16,8 @@
 <!-- The top and bottom paddings are defined in this container, but since we want
      the list view to span the full width (for touch interception purposes), we
      will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.allapps.AllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.AllAppsContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
@@ -39,6 +40,8 @@
         android:layout_height="match_parent"
         android:layout_gravity="center"
         android:focusable="true"
+        android:clipToPadding="false"
+        android:clipChildren="true"
         android:focusableInTouchMode="true"
         android:saveEnabled="false"
         android:visibility="gone">
@@ -46,59 +49,29 @@
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.allapps.AllAppsRecyclerView
             android:id="@+id/apps_list_view"
-            android:layout_below="@+id/search_container"
+            android:layout_below="@id/search_container_all_apps"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center_horizontal|top"
             android:clipToPadding="false"
+            android:overScrollMode="never"
             android:descendantFocusability="afterDescendants"
-            android:focusable="true"
-            android:paddingStart="@dimen/container_fastscroll_thumb_max_width"
-            android:paddingEnd="@dimen/container_fastscroll_thumb_max_width" />
+            android:focusable="true" />
 
-        <!-- Fast scroller popup -->
-        <TextView
-            style="@style/FastScrollerPopup"
-            android:layout_below="@+id/search_container"
-            android:id="@+id/fast_scroller_popup"
-            android:layout_alignParentEnd="true"
-            android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
+        <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
+         platform bug, which prevents using custom attributes in <include> tag -->
+        <include
+            layout="?android:attr/keyboardLayout"
+            android:id="@id/search_container_all_apps" />
 
-        <FrameLayout
-            android:id="@+id/search_container"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/all_apps_search_bar_height"
-            android:layout_gravity="center|top"
-            android:gravity="center|bottom"
-            android:orientation="horizontal"
-            android:saveEnabled="false">
-
-            <com.android.launcher3.ExtendedEditText
-                android:id="@+id/search_box_input"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/all_apps_search_bar_field_height"
-                android:background="@android:color/transparent"
-                android:layout_gravity="bottom"
-                android:focusableInTouchMode="true"
-                android:gravity="center"
-                android:imeOptions="actionSearch|flagNoExtractUi"
-                android:inputType="text|textNoSuggestions|textCapWords"
-                android:maxLines="1"
-                android:scrollHorizontally="true"
-                android:singleLine="true"
-                android:textColor="?android:attr/textColorSecondary"
-                android:hint="@string/all_apps_search_bar_hint"
-                android:textColorHint="@drawable/all_apps_search_hint"
-                android:textSize="16sp" />
-        </FrameLayout>
+        <include layout="@layout/all_apps_fast_scroller" />
 
     </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
     <View
-        style="@style/AllAppsNavBarProtection"
         android:id="@+id/nav_bar_bg"
+        android:background="?attr/allAppsNavBarScrimColor"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_gravity="bottom"
-        android:focusable="false"
-        android:visibility="invisible" />
+        android:focusable="false"  />
 </com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_discovery_item.xml b/res/layout/all_apps_discovery_item.xml
index fb1755c..728283f 100644
--- a/res/layout/all_apps_discovery_item.xml
+++ b/res/layout/all_apps_discovery_item.xml
@@ -94,8 +94,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
-        android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
-        android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+        android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+        android:paddingRight="@dimen/dynamic_grid_edge_margin"
         android:src="@drawable/all_apps_divider"
         android:scaleType="fitXY"
         android:focusable="false" />
diff --git a/res/layout/all_apps_discovery_loading_divider.xml b/res/layout/all_apps_discovery_loading_divider.xml
index 1ad5521..005847c 100644
--- a/res/layout/all_apps_discovery_loading_divider.xml
+++ b/res/layout/all_apps_discovery_loading_divider.xml
@@ -16,8 +16,8 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="6dp"
-    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
-    android:paddingRight="@dimen/container_fastscroll_thumb_max_width">
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin">
 
     <ProgressBar
         android:id="@+id/loadingProgressBar"
diff --git a/res/layout/all_apps_divider.xml b/res/layout/all_apps_divider.xml
index 1eaf685..8a4f646 100644
--- a/res/layout/all_apps_divider.xml
+++ b/res/layout/all_apps_divider.xml
@@ -19,8 +19,8 @@
     android:layout_height="wrap_content"
     android:paddingTop="@dimen/all_apps_divider_margin_vertical"
     android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
-    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
-    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin"
     android:src="@drawable/all_apps_divider"
     android:scaleType="fitXY"
     android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?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"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+    <!-- Fast scroller popup -->
+    <TextView
+        android:id="@+id/fast_scroller_popup"
+        style="@style/FastScrollerPopup"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+    <com.android.launcher3.views.RecyclerViewFastScroller
+        android:id="@+id/fast_scroller"
+        android:layout_width="@dimen/fastscroll_width"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentEnd="true"
+        android:layout_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="@dimen/fastscroll_end_margin"
+        launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index ca0cbcc..79fb612 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -23,7 +23,6 @@
     android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
     launcher:iconDisplay="all_apps"
     launcher:centerVertically="true"
-    android:paddingLeft="4dp"
-    android:paddingRight="4dp"
-    android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding" />
+    android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
+    android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
 
diff --git a/res/layout/all_apps_search_divider.xml b/res/layout/all_apps_search_divider.xml
deleted file mode 100644
index d2ef691..0000000
--- a/res/layout/all_apps_search_divider.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:importantForAccessibility="no"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
-    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
-    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
-    android:src="@drawable/all_apps_search_divider"
-    android:scaleType="fitXY"
-    android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 3f19b25..6f2dd3d 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -19,8 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="48dp"
     android:gravity="center"
-    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
-    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin"
     android:fontFamily="sans-serif-medium"
     android:textSize="14sp"
     android:textColor="?android:attr/colorAccent"
diff --git a/res/layout/app_icon.xml b/res/layout/app_icon.xml
index fa6eb89..52df694 100644
--- a/res/layout/app_icon.xml
+++ b/res/layout/app_icon.xml
@@ -14,4 +14,4 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.BubbleTextView style="@style/BaseIcon.Workspace" />
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 91a1e45..874fecc 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -20,38 +20,43 @@
     android:layout_height="match_parent"
     android:background="@drawable/widget_resize_shadow"
     android:foreground="@drawable/widget_resize_frame"
+    android:foregroundTint="?attr/workspaceTextColor"
     android:padding="0dp" >
 
     <!-- Left -->
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_widget_resize_handle"
         android:layout_gravity="left|center_vertical"
-        android:layout_marginLeft="@dimen/widget_handle_margin" />
+        android:layout_marginLeft="@dimen/widget_handle_margin"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:tint="?attr/workspaceTextColor" />
 
     <!-- Top -->
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_widget_resize_handle"
         android:layout_gravity="top|center_horizontal"
-        android:layout_marginTop="@dimen/widget_handle_margin" />
+        android:layout_marginTop="@dimen/widget_handle_margin"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:tint="?attr/workspaceTextColor" />
 
     <!-- Right -->
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_widget_resize_handle"
         android:layout_gravity="right|center_vertical"
-        android:layout_marginRight="@dimen/widget_handle_margin" />
+        android:layout_marginRight="@dimen/widget_handle_margin"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:tint="?attr/workspaceTextColor" />
 
     <!-- Bottom -->
     <ImageView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:src="@drawable/ic_widget_resize_handle"
         android:layout_gravity="bottom|center_horizontal"
-        android:layout_marginBottom="@dimen/widget_handle_margin" />
+        android:layout_marginBottom="@dimen/widget_handle_margin"
+        android:src="@drawable/ic_widget_resize_handle"
+        android:tint="?attr/workspaceTextColor" />
 
 </com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 85caba4..4a2ad42 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
-    android:layout_height="@dimen/bg_popup_item_height" >
+    android:layout_height="@dimen/bg_popup_item_height"
+    android:theme="@style/PopupItem" >
 
     <com.android.launcher3.shortcuts.DeepShortcutTextView
         style="@style/BaseIcon"
@@ -50,6 +51,6 @@
         android:layout_height="@dimen/popup_item_divider_height"
         android:layout_gravity="end|bottom"
         android:visibility="gone"
-        android:background="?android:attr/listDivider" />
+        android:background="?attr/popupColorTertiary" />
 
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
index ee22d1e..ed18192 100644
--- a/res/layout/drop_target_bar_horz.xml
+++ b/res/layout/drop_target_bar_horz.xml
@@ -17,8 +17,10 @@
 <com.android.launcher3.DropTargetBar
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:theme="@style/HomeScreenElementTheme"
     android:layout_width="match_parent"
     android:layout_height="@dimen/dynamic_grid_drop_target_size"
+    android:visibility="invisible"
     android:layout_gravity="center_horizontal|top"
     android:focusable="false">
 
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
index 10b1d7c..2394d0d 100644
--- a/res/layout/drop_target_bar_vert.xml
+++ b/res/layout/drop_target_bar_vert.xml
@@ -15,11 +15,13 @@
      limitations under the License.
 -->
 <com.android.launcher3.DropTargetBar
+    android:theme="@style/HomeScreenElementTheme"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/dynamic_grid_drop_target_size"
     android:orientation="vertical"
     android:layout_height="match_parent"
     android:layout_gravity="left"
+    android:visibility="invisible"
     android:focusable="false"
     android:paddingTop="@dimen/vert_drop_target_vertical_gap" >
 
@@ -30,8 +32,7 @@
         android:gravity="center"
         android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
         android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
-        android:id="@+id/delete_target_text"
-        android:textColor="@android:color/white" />
+        android:id="@+id/delete_target_text" />
 
     <!-- Uninstall target -->
     <com.android.launcher3.UninstallDropTarget
@@ -41,7 +42,6 @@
         android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
         android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
         android:id="@+id/uninstall_target_text"
-        android:textColor="@android:color/white"
         android:layout_marginTop="@dimen/vert_drop_target_vertical_gap"/>
 
     <Space
@@ -57,7 +57,6 @@
         android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
         android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
         android:id="@+id/info_target_text"
-        android:textColor="@android:color/white"
         android:layout_marginBottom="64dp"/>
 
 </com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index ccc6b01..4093744 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -20,7 +20,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:focusable="true" >
-    <com.android.launcher3.BubbleTextView
+    <com.android.launcher3.views.DoubleShadowBubbleTextView
         style="@style/BaseIcon.Workspace"
         android:id="@+id/folder_icon_name"
         android:focusable="false"
diff --git a/res/layout/qsb_blocker_view.xml b/res/layout/gradient_bg.xml
similarity index 69%
rename from res/layout/qsb_blocker_view.xml
rename to res/layout/gradient_bg.xml
index 453eebe..db448d7 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/layout/gradient_bg.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,7 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.qsb.QsbBlockerView
+
+<com.android.launcher3.graphics.GradientView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/gradient_bg"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:layout_height="match_parent"
+    android:visibility="gone"
+    launcher:layout_ignoreInsets="true" />
\ No newline at end of file
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index f5b5bbf..582a83f 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -14,6 +14,7 @@
      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
diff --git a/res/layout/notification.xml b/res/layout/notification.xml
index f955c6b..1eebb43 100644
--- a/res/layout/notification.xml
+++ b/res/layout/notification.xml
@@ -19,9 +19,8 @@
     android:id="@+id/notification_view"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="wrap_content"
-    android:elevation="@dimen/deep_shortcuts_elevation"
-    android:background="@drawable/bg_white_round_rect"
-    android:backgroundTint="@color/notification_color_beneath">
+    android:theme="@style/PopupItem"
+    android:elevation="@dimen/deep_shortcuts_elevation">
 
     <RelativeLayout
         android:layout_width="match_parent"
@@ -29,14 +28,22 @@
         android:orientation="vertical"
         android:clipChildren="false">
 
+        <View
+            android:id="@+id/gutter_top"
+            android:layout_width="match_parent"
+            android:layout_height="4dp"
+            android:theme="@style/PopupGutter"
+            android:visibility="gone" />
+
         <FrameLayout
             android:id="@+id/header"
             android:layout_width="match_parent"
             android:layout_height="@dimen/notification_header_height"
             android:paddingStart="@dimen/notification_padding_start"
             android:paddingEnd="@dimen/notification_padding_end"
-            android:background="@color/popup_header_background_color"
-            android:elevation="@dimen/notification_elevation">
+            android:background="?attr/popupColorPrimary"
+            android:elevation="@dimen/notification_elevation"
+            android:layout_below="@id/gutter_top" >
             <TextView
                 android:id="@+id/notification_text"
                 android:layout_width="wrap_content"
@@ -45,7 +52,7 @@
                 android:gravity="center_vertical"
                 android:text="@string/notifications_header"
                 android:textSize="@dimen/notification_header_text_size"
-                android:textColor="?android:attr/textColorSecondary" />
+                android:textColor="?android:attr/textColorPrimary" />
             <TextView
                 android:id="@+id/notification_count"
                 android:layout_width="@dimen/notification_icon_size"
@@ -67,8 +74,9 @@
             android:id="@+id/divider"
             android:layout_width="match_parent"
             android:layout_height="@dimen/popup_item_divider_height"
-            android:background="?android:attr/listDivider"
-            android:layout_below="@id/main_view"/>
+            android:background="?attr/popupColorTertiary"
+            android:layout_below="@id/main_view"
+            android:visibility="gone" />
 
         <include layout="@layout/notification_footer"
             android:id="@+id/footer"
@@ -76,6 +84,14 @@
             android:layout_height="@dimen/notification_footer_height"
             android:layout_below="@id/divider" />
 
+        <View
+            android:id="@+id/gutter_bottom"
+            android:layout_width="match_parent"
+            android:layout_height="4dp"
+            android:theme="@style/PopupGutter"
+            android:visibility="gone"
+            android:layout_below="@id/footer" />
+
     </RelativeLayout>
 
 </com.android.launcher3.notification.NotificationItemView>
diff --git a/res/layout/notification_footer.xml b/res/layout/notification_footer.xml
index ed2212b..86280e0 100644
--- a/res/layout/notification_footer.xml
+++ b/res/layout/notification_footer.xml
@@ -22,7 +22,7 @@
     android:elevation="@dimen/notification_elevation"
     android:clipChildren="false"
     android:layout_gravity="center_vertical"
-    android:background="@color/popup_background_color">
+    android:background="?attr/popupColorPrimary">
 
     <LinearLayout
         android:id="@+id/icon_row"
diff --git a/res/layout/notification_main.xml b/res/layout/notification_main.xml
index ce4e137..f94face 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -28,16 +28,17 @@
         android:layout_height="match_parent"
         android:orientation="vertical"
         android:gravity="center_vertical"
-        android:background="@color/popup_background_color"
+        android:background="?attr/popupColorPrimary"
         android:paddingStart="@dimen/notification_padding_start"
-        android:paddingEnd="@dimen/notification_main_text_padding_end">
+        android:paddingEnd="@dimen/notification_main_text_padding_end"
+        android:paddingBottom="14dp">
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:textAlignment="viewStart"
             android:fontFamily="sans-serif"
-            android:textSize="@dimen/notification_main_text_size"
+            android:textSize="@dimen/notification_main_title_size"
             android:textColor="?android:attr/textColorPrimary"
             android:lines="1"
             android:ellipsize="end" />
@@ -58,6 +59,7 @@
         android:layout_width="@dimen/notification_icon_size"
         android:layout_height="@dimen/notification_icon_size"
         android:layout_marginEnd="@dimen/notification_padding_end"
+        android:layout_marginBottom="7dp"
         android:layout_gravity="center_vertical|end" />
 
 </com.android.launcher3.notification.NotificationMainView>
diff --git a/res/layout/qsb_blocker_view.xml b/res/layout/notification_pref_warning.xml
similarity index 63%
copy from res/layout/qsb_blocker_view.xml
copy to res/layout/notification_pref_warning.xml
index 453eebe..100fac5 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/layout/notification_pref_warning.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+        http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.qsb.QsbBlockerView
+<ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:layout_width="48dp"
+    android:layout_height="match_parent"
+    android:importantForAccessibility="no"
+    android:scaleType="center"
+    android:src="@drawable/ic_warning"
+    android:tint="?android:attr/textColorSecondary" />
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 2091721..d1ac56c 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,8 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:launcher="http://schemas.android.com/apk/res-auto"
+      android:theme="@style/HomeScreenElementTheme"
       launcher:layout_ignoreInsets="true"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
@@ -30,12 +32,13 @@
         android:layout_weight="1"
         android:drawablePadding="4dp"
         android:drawableTop="@drawable/ic_wallpaper"
+        android:drawableTint="?attr/workspaceTextColor"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
         android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/wallpaper_button_text"
         android:textAllCaps="true"
-        android:textColor="@android:color/white"
+        android:textColor="?attr/workspaceTextColor"
         android:textSize="12sp" />
 
     <TextView
@@ -45,12 +48,13 @@
         android:layout_weight="1"
         android:drawablePadding="4dp"
         android:drawableTop="@drawable/ic_widget"
+        android:drawableTint="?attr/workspaceTextColor"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
         android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/widget_button_text"
         android:textAllCaps="true"
-        android:textColor="@android:color/white"
+        android:textColor="?attr/workspaceTextColor"
         android:textSize="12sp" />
 
     <TextView
@@ -60,12 +64,13 @@
         android:layout_weight="1"
         android:drawablePadding="4dp"
         android:drawableTop="@drawable/ic_setting"
+        android:drawableTint="?attr/workspaceTextColor"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
         android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/settings_button_text"
         android:textAllCaps="true"
-        android:textColor="@android:color/white"
+        android:textColor="?attr/workspaceTextColor"
         android:textSize="12sp" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index 2e1b57f..92f52d6 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -16,12 +16,13 @@
 
 <com.android.launcher3.pageindicators.PageIndicatorLineCaret
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:theme="@style/HomeScreenElementTheme"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/dynamic_grid_page_indicator_height">
+    android:layout_height="@dimen/dynamic_grid_min_page_indicator_size">
         <ImageView
             android:id="@+id/all_apps_handle"
             android:layout_width="48dp"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
+            android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+            android:layout_gravity="top|center"
             android:scaleType="centerInside"/>
 </com.android.launcher3.pageindicators.PageIndicatorLineCaret>
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index e9cfe24..67db4a5 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -19,8 +19,8 @@
     android:id="@+id/deep_shortcuts_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
+    android:paddingTop="4dp"
+    android:paddingBottom="4dp"
     android:clipToPadding="false"
     android:clipChildren="false"
     android:elevation="@dimen/deep_shortcuts_elevation"
diff --git a/res/layout/qsb_default_view.xml b/res/layout/qsb_default_view.xml
index 3075f80..04fe236 100644
--- a/res/layout/qsb_default_view.xml
+++ b/res/layout/qsb_default_view.xml
@@ -21,7 +21,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="48dp"
-        android:layout_margin="16dp"
+        android:layout_margin="8dp"
         android:layout_gravity="center_vertical"
         android:background="@drawable/round_rect_primary"
         android:elevation="2dp"
diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml
new file mode 100644
index 0000000..fc07002
--- /dev/null
+++ b/res/layout/search_container_all_apps.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.allapps.search.AppsSearchContainerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/search_container_all_apps"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/all_apps_search_bar_height"
+    android:layout_gravity="center|top"
+    android:layout_marginBottom="-8dp"
+    android:gravity="center|bottom"
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin"
+    android:saveEnabled="false" >
+
+    <!--
+      Note: The following relation should always be true so that the shadows are aligned properly
+      search_box_input.layout_marginBottom
+            == search_divider.layout_marginBottom
+            == - (search_container.layout_marginBottom)
+            >= 5dp
+    -->
+    <com.android.launcher3.ExtendedEditText
+        android:id="@+id/search_box_input"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/all_apps_search_bar_field_height"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="8dp"
+        android:background="@android:color/transparent"
+        android:focusableInTouchMode="true"
+        android:gravity="center"
+        android:hint="@string/all_apps_search_bar_hint"
+        android:imeOptions="actionSearch|flagNoExtractUi"
+        android:inputType="text|textNoSuggestions|textCapWords"
+        android:maxLines="1"
+        android:saveEnabled="false"
+        android:scrollHorizontally="true"
+        android:singleLine="true"
+        android:textColor="?android:attr/textColorSecondary"
+        android:textColorHint="@drawable/all_apps_search_hint"
+        android:textSize="16sp" />
+
+    <!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
+         We translate the header down, and its content up by the same amount, so that it gets
+         clipped by the parent, making it look like the divider was scrolled out of the view. -->
+    <FrameLayout
+        android:id="@+id/search_divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="8dp" >
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="@drawable/all_apps_search_divider"/>
+    </FrameLayout>
+</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
diff --git a/res/layout/qsb_container.xml b/res/layout/search_container_workspace.xml
similarity index 95%
rename from res/layout/qsb_container.xml
rename to res/layout/search_container_workspace.xml
index 6fa843d..1c617b1 100644
--- a/res/layout/qsb_container.xml
+++ b/res/layout/search_container_workspace.xml
@@ -19,7 +19,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:id="@+id/qsb_container"
+        android:id="@id/search_container_workspace"
         android:padding="0dp" >
 
     <fragment
diff --git a/res/layout/shortcuts_item.xml b/res/layout/shortcuts_item.xml
index 8b20bcb..7cd996d 100644
--- a/res/layout/shortcuts_item.xml
+++ b/res/layout/shortcuts_item.xml
@@ -19,14 +19,21 @@
     android:id="@+id/shortcuts_view"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="wrap_content"
-    android:elevation="@dimen/deep_shortcuts_elevation"
-    android:background="@drawable/bg_white_round_rect">
+    android:elevation="@dimen/deep_shortcuts_elevation">
 
     <LinearLayout
-        android:id="@+id/deep_shortcuts"
+        android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">
+
+        <!-- The shortcuts header is added at runtime when necessary. -->
+
+        <LinearLayout
+            android:id="@+id/shortcuts"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical" />
     </LinearLayout>
 
 </com.android.launcher3.shortcuts.ShortcutsItemView>
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index bf849aa..04f3d02 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -18,7 +18,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
-    android:layout_height="@dimen/bg_popup_item_height" >
+    android:layout_height="@dimen/bg_popup_item_height"
+    android:theme="@style/PopupItem" >
 
     <com.android.launcher3.BubbleTextView
         style="@style/BaseIcon"
@@ -40,7 +41,8 @@
         android:layout_width="@dimen/system_shortcut_icon_size"
         android:layout_height="@dimen/system_shortcut_icon_size"
         android:layout_marginStart="@dimen/system_shortcut_margin_start"
-        android:layout_gravity="start|center_vertical" />
+        android:layout_gravity="start|center_vertical"
+        android:backgroundTint="?android:attr/textColorTertiary"/>
 
     <View
         android:id="@+id/divider"
@@ -48,6 +50,6 @@
         android:layout_height="@dimen/popup_item_divider_height"
         android:layout_gravity="end|bottom"
         android:visibility="gone"
-        android:background="?android:attr/listDivider" />
+        android:background="?attr/popupColorTertiary" />
 
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_icon_only.xml b/res/layout/system_shortcut_icon_only.xml
index 313c69c..c59cb53 100644
--- a/res/layout/system_shortcut_icon_only.xml
+++ b/res/layout/system_shortcut_icon_only.xml
@@ -19,4 +19,6 @@
     android:layout_width="@dimen/system_shortcut_header_icon_touch_size"
     android:layout_height="@dimen/system_shortcut_header_icon_touch_size"
     android:background="?android:attr/selectableItemBackgroundBorderless"
-    android:padding="@dimen/system_shortcut_header_icon_padding" />
+    android:tint="?android:attr/textColorHint"
+    android:padding="@dimen/system_shortcut_header_icon_padding"
+    android:theme="@style/PopupItem" />
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index 676be8e..34d63e7 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -21,4 +21,7 @@
     android:layout_height="@dimen/system_shortcut_header_height"
     android:orientation="horizontal"
     android:gravity="end|center_vertical"
-    android:background="@color/popup_header_background_color" />
+    android:background="?attr/popupColorSecondary"
+    android:elevation="1dp"
+    android:outlineProvider="none" />
+    <!-- We have elevation so this is drawn on top, but no outline provider to remove shadow -->
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index cde6540..afa19b8 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -24,11 +24,12 @@
 
     <com.android.launcher3.folder.FolderPagedView
         android:id="@+id/folder_content"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="8dp"
         android:paddingRight="8dp"
-        android:paddingTop="6dp"
+        android:paddingTop="16dp"
         launcher:pageIndicator="@+id/folder_page_indicator" />
 
     <LinearLayout
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2063f32..2e6ce94 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -24,11 +24,12 @@
 
     <com.android.launcher3.folder.FolderPagedView
         android:id="@+id/folder_content"
+        android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:paddingLeft="8dp"
         android:paddingRight="8dp"
-        android:paddingTop="6dp"
+        android:paddingTop="16dp"
         launcher:pageIndicator="@+id/folder_page_indicator" />
 
     <LinearLayout
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index c2270d2..e8c6961 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -22,14 +22,15 @@
     android:paddingTop="28dp"
     android:background="?android:attr/colorPrimary"
     android:elevation="@dimen/deep_shortcuts_elevation"
-    android:layout_gravity="bottom">
+    android:layout_gravity="bottom"
+    android:theme="?attr/widgetsTheme">
 
     <TextView
+        style="@style/TextTitle"
         android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:fontFamily="sans-serif"
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="24sp"/>
 
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index b6e0a0b..4cd03ce 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -41,7 +41,7 @@
         android:singleLine="true"
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="16sp"
-        launcher:customShadows="false"
+        android:textAlignment="viewStart"
         launcher:deferShadowGeneration="true"
         launcher:iconDisplay="widget_section"
         launcher:iconSizeOverride="@dimen/widget_section_icon_size"
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 2f11c28..4f3c7c8 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -23,25 +23,25 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:descendantFocusability="afterDescendants"
-    launcher:revealBackground="@drawable/round_rect_primary"
-    android:theme="@style/WidgetContainerTheme">
+    android:theme="?attr/widgetsTheme"
+    launcher:revealBackground="@drawable/round_rect_primary">
 
     <View
         android:id="@+id/reveal_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"
-        android:focusable="false"
         android:elevation="2dp"
+        android:focusable="false"
         android:visibility="invisible" />
 
     <FrameLayout
         android:id="@+id/main_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:layout_gravity="center"
         android:elevation="15dp"
-        android:visibility="gone"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:visibility="gone">
 
         <com.android.launcher3.widget.WidgetsRecyclerView
             android:id="@+id/widgets_list_view"
@@ -50,18 +50,24 @@
 
         <!-- Fast scroller popup -->
         <TextView
-            style="@style/FastScrollerPopup"
-            android:layout_below="@+id/search_container"
             android:id="@+id/fast_scroller_popup"
+            style="@style/FastScrollerPopup"
             android:layout_gravity="top|end"
-            android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
+            android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
 
         <ProgressBar
+            android:id="@+id/loader"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:id="@+id/loader"
             android:layout_gravity="center" />
-    </FrameLayout>
 
+        <com.android.launcher3.views.RecyclerViewFastScroller
+            android:id="@+id/fast_scroller"
+            android:layout_width="@dimen/fastscroll_width"
+            android:layout_height="match_parent"
+            android:layout_gravity="end"
+            android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+    </FrameLayout>
 
 </com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
new file mode 100644
index 0000000..3c1b64f
--- /dev/null
+++ b/res/raw/downgrade_schema.json
@@ -0,0 +1,20 @@
+{
+  // Note: Comments are not supported in JSON schema, but android parser is lenient.
+
+  // Maximum DB version supported by this schema
+  "version" : 27,
+
+  // Downgrade from 27 to 26. Empty array indicates, the DB is compatible
+  "downgrade_to_26" : [],
+  "downgrade_to_25" : [],
+  "downgrade_to_24" : [],
+  "downgrade_to_23" : [],
+  "downgrade_to_22" : [
+    "ALTER TABLE favorites RENAME TO temp_favorites;",
+    "CREATE TABLE favorites(_id INTEGER PRIMARY KEY, title TEXT, intent TEXT, container INTEGER, screen INTEGER, cellX INTEGER, cellY INTEGER, spanX INTEGER, spanY INTEGER, itemType INTEGER, appWidgetId INTEGER NOT NULL DEFAULT - 1, iconPackage TEXT, iconResource TEXT, icon BLOB, appWidgetProvider TEXT, modified INTEGER NOT NULL DEFAULT 0, restored INTEGER NOT NULL DEFAULT 0, profileId INTEGER DEFAULT 0, rank INTEGER NOT NULL DEFAULT 0);",
+    "INSERT INTO favorites SELECT _id, title, intent, container, screen, cellX, cellY, spanX, spanY, itemType, appWidgetId, iconPackage, iconResource, icon, appWidgetProvider, modified, restored, profileId, rank FROM temp_favorites;",
+    "DROP TABLE temp_favorites;"
+  ]
+
+  // Missing values indicate the DB is not compatible
+}
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 29e27ea..d71b4c7 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Raak en hou om self te plaas"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Voeg outomaties by"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Deursoek programme"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Laai tans programme …"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Geen programme gevind wat met \"<xliff:g id="QUERY">%1$s</xliff:g>\" ooreenstem nie"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Deursoek programme"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Laai tans programme …"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Kon geen programme kry wat by \"<xliff:g id="QUERY">%1$s</xliff:g>\" pas nie"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Soek meer programme"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Instellings"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Home-instellings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Oorsig"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige vertooninstelling laat nie rotasie toe nie"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikoon-kentekentoekenning"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Aan vir alle programme"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Af vir alle programme"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Voeg ikoon by tuisskerm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Vir nuwe programme"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Verander ikoon se vorm"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Gebruik stelselverstek"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 59c44e2..293b452 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"መተግበሪያዎችን ይፈልጉ"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"መተግበሪያዎችን በመጫን ላይ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</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="out_of_space" msgid="4691004494942118364">"በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"ቅንብሮች"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"አጠቃላይ ዕይታ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ስልኩ ሲዞር"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"የአሁኑ የማሳያ ቅንብር ማሽከርከርን አይፈቅድም"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"የአዶ ባጅ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"ለሁሉም መተግበሪያዎች በርቷል"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"ለሁሉም መተግበሪያዎች ጠፍጧል"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"አዶ ወደ የመነሻ ማያ ገጽ አክል"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ለአዲስ መተግበሪያዎች"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"የአዶ ቅርፅ ለውጥ"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"የሥርዓቱን ነባሪ ተጠቀም"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index d8b1cc9..6447bf0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"البحث في التطبيقات"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"جارٍ تحميل التطبيقات…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"لم يتم العثور على أية تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"ليس هناك مساحة أخرى في هذه الشاشة الرئيسية."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"الإعدادات"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"إعدادات الصفحة الرئيسية"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"نظرة عامة"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"لا يسمح إعداد العرض الحالي بالتدوير"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"وضع شارات على رموز التطبيقات"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"تشغيل لجميع التطبيقات"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"إيقاف لجميع التطبيقات"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"إضافة رمز إلى الشاشة الرئيسية"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"للتطبيقات الجديدة"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"تغيير شكل الرمز"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد الافتراضي للنظام"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index e5d0a6c..18f87bc 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Tətbiq Axtarın"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</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="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Ayarlar"</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="accessibility_action_overview" msgid="6257665857640347026">"İcmal"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cari Ekran ayarı fırlatmağa icazə vermir"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"İkona nişanı"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Bütün tətbiqlər üçün aktivdir"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Bütün tətbiqlər üçün deaktivdir"</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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"İkona formasını dəyişin"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 401fef3..777512a 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i zadržite da biste postavili ručno"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatski dodaj"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretražite aplikacije"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikacije se učitavaju…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži još aplikacija"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nema više prostora na ovom početnom ekranu."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Podešavanja"</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="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuelno podešavanje prikaza ne dozvoljava rotaciju"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Dodavanje znački na ikone"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Uključeno za sve aplikacije"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Isključeno za sve aplikacije"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promenite oblik ikona"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Koristi podrazumevano sistemsko podešavanje"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index f5dba2d..0a31ddf 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -27,21 +27,26 @@
     <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="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук у Праграмах"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Ідзе загрузка праграм…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</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="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_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="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
@@ -65,12 +70,27 @@
     <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="8119458837558863227">"Налады"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Агляд"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Бягучая налада дысплэя не прадугледжвае паварот"</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="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_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>
@@ -78,6 +98,7 @@
     <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>
@@ -104,4 +125,7 @@
     <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>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index c19a8b4..3e05e0b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Търсене в приложенията"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Приложенията се зареждат…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</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="out_of_space" msgid="4691004494942118364">"На този начален екран няма повече място."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Настройки"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Настройки за Google Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Общ преглед"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешаване на завъртането на началния екран"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"При завъртане на телефона"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Текущата настройка на екрана не разрешава завъртане"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Означаване със значка на иконите"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Включено за всички приложения"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Изключено за всички приложения"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавяне на икона към началния екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови приложения"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Промяна на формата на иконите"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Използване на стандартната системна настройка"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 70e2c9b..471602f 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -27,7 +27,7 @@
     <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="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>
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"অ্যাপ্লিকেশানগুলি লোড হচ্ছে..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি"</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="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
@@ -60,7 +60,7 @@
     <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>
     <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_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>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"সেটিংস"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"হোম এর সেটিংস"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"এক নজরে"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"যখন ফোনটি ঘোরানো হয়"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"বর্তমান প্রদর্শনের সেটিংস ঘোরানোর মঞ্জুরি দেয় না"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"আইকনে ব্যাজ দেওয়া"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"সমস্ত অ্যাপের জন্য চালু আছে"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"সমস্ত অ্যাপের জন্য বন্ধ আছে"</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="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_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>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 37850db..49c862a 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Pretraži aplikacije"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija koja odgovara upitu \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Postavke"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Postavke za Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutne postavke ekrana ne dozvoljavaju rotiranje"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Označavanje ikona"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Uključeno za sve aplikacije"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Isključeno za sve aplikacije"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodajte ikonu na početni ekran"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promjena oblika ikona"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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">"Traži"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 9913d6d..b1a1f85 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Afegeix automàticament"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca a les aplicacions"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"S\'estan carregant les aplicacions..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No s\'ha trobat cap aplicació que coincideixi amb <xliff:g id="QUERY">%1$s</xliff:g>"</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="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Configuració"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Visió general"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuració actual de la pantalla no permet la rotació"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Icones encerclades"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activades per a totes les aplicacions"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desactivades per a totes les aplicacions"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix la icona a la pantalla d\'inici"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Canvia la forma de les icones"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Utilitza l\'opció predeterminada del sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 03c109d..e6dee3c 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Chcete-li položku umístit ručně, klepněte na ni a podržte ji"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Přidat automaticky"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hledat aplikace"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítání aplikací…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hledat v aplikacích"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Načítání aplikací…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Vyhledat další aplikace"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Nastavení"</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="accessibility_action_overview" msgid="6257665857640347026">"Přehled"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuální nastavení displeje neumožňuje otáčení"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Odznaky na ikonách"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Zapnuto pro všechny aplikace"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Vypnuto pro všechny aplikace"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Přidat ikonu na plochu"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pro nové aplikace"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Změnit tvar ikony"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Použít výchozí nastavení systému"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c16b8c1..653105e 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Tilføj automatisk"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søg i Apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Indlæser apps…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Indstillinger"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærmen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Oversigt"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Den aktuelle indstilling for visning tillader ikke rotation"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonbadges"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Slået til for alle apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Slået fra for alle apps"</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="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Skift ikonform"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Brug systemstandarden"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c627b74..1c4cd7a 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Zum manuellen Hinzufügen gedrückt halten"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatisch hinzufügen"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"In Apps suchen"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps werden geladen..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps finden"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Apps werden geladen…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Weitere Apps suchen"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Einstellungen"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen für den Startbildschirm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Übersicht"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Die aktuelle \"Display\"-Einstellung verhindert eine Drehung der Anzeige"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Logosymbole"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Für alle Apps aktiviert"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Für alle Apps deaktiviert"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Form des Symbols ändern"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Systemstandardeinstellung verwenden"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 9f5c01f..25c8866 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Αναζήτηση εφαρμογών"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Φόρτωση εφαρμογών…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Δεν βρέθηκαν εφαρμογές για το ερώτημα \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Ρυθμίσεις"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχικής σελίδας"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Επισκόπηση"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Όταν το τηλέφωνο περιστρέφεται"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Η τρέχουσα ρύθμιση οθόνης δεν επιτρέπει την περιστροφή"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Σήματα εικονιδίων"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Ενεργοποιημένα για όλες τις εφαρμογές"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Απενεργοποιημένα για όλες τις εφαρμογές"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Προσθήκη εικονιδίου στην Αρχική οθόνη"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Για νέες εφαρμογές"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Αλλαγή σχήματος εικονιδίου"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Χρήση προεπιλογής συστήματος"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 1a1c1c9..f3f10cc 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch &amp; hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Settings"</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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Icon badging"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"On for all apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Off for all apps"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 1a1c1c9..f3f10cc 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch &amp; hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Settings"</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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Icon badging"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"On for all apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Off for all apps"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 1a1c1c9..f3f10cc 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch &amp; hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"No apps found matching \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Settings"</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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Icon badging"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"On for all apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Off for all apps"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 28d8be3..f60e186 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén presionado para ubicarlo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Agregar automáticamente"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Buscar aplicaciones"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No hay aplicaciones que coincidan con <xliff:g id="QUERY">%1$s</xliff:g>."</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar apps"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando apps…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"No hay apps que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más apps"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuración de Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Recientes"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración actual no permite la rotación de la pantalla"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Insignias de íconos"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activada para todas las apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desactivada para todas las apps"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los íconos"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar el sistema predeterminado"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index badb33e..3ab41ca 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantenlo pulsado para añadirlo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Añadir automáticamente"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca aplicaciones"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicaciones"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando aplicaciones…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más aplicaciones"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Ajustes"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión general"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración de pantalla actual no permite girar la pantalla"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Insignias de los iconos"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activadas para todas las aplicaciones"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desactivadas para todas las aplicaciones"</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="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="title_change_settings" msgid="1376365968844349552">"Cambiar ajustes"</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 nuevas aplicaciones"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los iconos"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar opción predeterminada del sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index feb63ca..9d9991d 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -27,14 +27,19 @@
     <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="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</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="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>
@@ -65,12 +70,27 @@
     <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="8119458837558863227">"Seaded"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Avalehe seaded"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Ülevaade"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Praegune kuvaseade ei luba pööramist"</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="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_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>
@@ -78,6 +98,7 @@
     <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>
@@ -104,4 +125,7 @@
     <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>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index aa9c796..b6082fad 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Bilatu aplikazioetan"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</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="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Ezarpenak"</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="accessibility_action_overview" msgid="6257665857640347026">"Ikuspegi orokorra"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Uneko pantaila-ezarpenak ez du onartzen ikuspegia biratzea"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonoen bereizgarriak"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Aktibatuta aplikazio guztietan"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desaktibatuta aplikazio guztietan"</string>
+    <string name="icon_badging_title" msgid="874121399231955394">"Jakinarazteko 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">"Jakinarazteko 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="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Aldatu ikonoaren forma"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index e66ea80..8fe874b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"جستجوی برنامه‌ها"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"در حال بارگیری برنامه‌ها..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"هیچ برنامه‌ای مطابق با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</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="out_of_space" msgid="4691004494942118364">"فضای بیشتری در این صفحه اصلی موجود نیست."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"تنظیمات"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"نمای کلی"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"وقتی تلفن چرخانده می‌شود"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"تنظیم نمایشگر کنونی اجازه چرخش نمی‌دهد"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"برچسب‌گذاری نماد"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"برای همه برنامه‌ها روشن‌ است"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"برای همه برنامه‌ها خاموش است"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"افزودن نماد به صفحه اصلی"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"برای برنامه‌های جدید"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"تغییر شکل نماد"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"استفاده از پیش‌فرض سیستم"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index a7db232..016dbbc 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sijoita manuaalisesti koskettamalla pitkään."</string>
     <string name="place_automatically" msgid="8064208734425456485">"Lisää automaattisesti"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sovellushaku"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Ladataan sovelluksia…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"”<xliff:g id="QUERY">%1$s</xliff:g>” ei palauttanut sovelluksia."</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hae sovelluksia"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Ladataan sovelluksia…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> ei palauttanut sovelluksia."</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hae lisää sovelluksia"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Asetukset"</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="accessibility_action_overview" msgid="6257665857640347026">"Yleiskatsaus"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Nykyiset näyttöasetukset eivät salli näytön kiertämistä."</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Kuvakemerkit"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Käytössä kaikissa sovelluksissa"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Pois käytöstä kaikissa sovelluksissa"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisää kuvake aloitusruutuun"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uusille sovelluksille"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Muuta kuvakkeen muotoa"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Käytä järjestelmän oletusarvoa"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 03757e0..e57433d 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Maintenez le doigt sur l\'élément pour le placer manuellement"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ajouter automatiquement"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher des applications"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications en cours…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Paramètres"</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="accessibility_action_overview" msgid="6257665857640347026">"Présentation"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le mode d\'affichage actuel ne permet pas le pivotement"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Insignes d\'icône"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activé pour toutes les applications"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Désactivé pour toutes les applications"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme de l\'icône"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser les valeurs système par défaut"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 75a693c..dde4392 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Appuyez de manière prolongée pour placer l\'élément manuellement"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ajouter automatiquement"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les applications"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Paramètres"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'écran d\'accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Vue d\'ensemble"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le paramètre d\'affichage actuel n\'autorise pas la rotation."</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Identification des icônes"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activée pour toutes les applications"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Désactivée pour toutes les applications"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme des icônes"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser la valeur système par défaut"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 4bff56f..d55514d 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Aplicacións de busca"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Configuración"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuración de inicio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión xeral"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuración de visualización actual non permite xirar a pantalla"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Insignias de iconas"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activado para todas as aplicacións"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desactivado para todas as aplicacións"</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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma das iconas"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index b243da7..7096a3e 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -29,15 +29,15 @@
     <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_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="7084713969757597256">"શોધ ઍપ્લિકેશનો"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</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="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"સેટિંગ્સ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ્સ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
-    <string name="accessibility_action_overview" msgid="6257665857640347026">"વિહંગાવલોકન"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ઝલક"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"આઇકનને બૅજ કરવું"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"બધી ઍપ્લિકેશનો માટે ચાલુ છે"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"બધી ઍપ્લિકેશનો માટે બંધ છે"</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="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_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>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 9a14ae0..f1ee0f2 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ऐप्‍स खोजें"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ऐप्स लोड हो रहे हैं..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलान करने वाला कोई ऐप नहीं मिला"</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="out_of_space" msgid="4691004494942118364">"इस होम स्‍क्रीन पर स्थान शेष नहीं है."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"सेटिंग"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके व्यवस्थापक द्वारा अक्षम"</string>
-    <string name="accessibility_action_overview" msgid="6257665857640347026">"अवलोकन"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"खास जानकारी"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"आइकन बैजिंग"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"सभी ऐप्लिकेशन के लिए चालू है"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"सभी ऐप्लिकेशन के लिए बंद है"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीन में आइकन जोड़ें"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप्लिकेशन के लिए"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकन का आकार बदलें"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टम डिफ़ॉल्ट का उपयोग करें"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 4b6a0bd..aaae258 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i zadržite stavku da biste je postavili ručno"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatski"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Učitavanje aplikacija…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretraži aplikacije"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Učitavanje aplikacija…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Traži više aplikacija"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Postavke Homea"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutačna postavka zaslona ne dopušta zakretanje"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Dodavanje oznake na ikonu"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Uključeno za sve aplikacije"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Isključeno za sve aplikacije"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promijeni oblik ikona"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Upotrijebi zadane postavke sustava"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index c5ac907..2da64b0 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tartsa lenyomva a manuális hozzáadáshoz"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatikus hozzáadás"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Alkalmazások keresése"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Alkalmazások betöltése…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Egy alkalmazás sem található a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre."</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Alkalmazások keresése"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Alkalmazások betöltése…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nem található alkalmazás a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"További alkalmazások keresése"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Beállítások"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"A Home beállításai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Áttekintés"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"A jelenlegi kijelzőbeállítások nem teszik lehetővé az elforgatást"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonjelvények"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Bekapcsolva az összes alkalmazásnál"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Kikapcsolva az összes alkalmazásnál"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikon formájának módosítása"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Alapértelmezett érték használata"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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_search" msgid="891119232568284442">"Keresés"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 46e5682..2b55e96 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Հավելվածների որոնում"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Հավելվածների բեռնում…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</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="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Կարգավորումներ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էջի կարգավորումներ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Համատեսք"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Պատկերակի նշանակ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Միացված է բոլոր հավելվածների համար"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Անջատված է բոլոր հավելվածների համար"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Նոր հավելվածների համար"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Փոխել պատկերակների տեսքը"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Օգտագործել համակարգի կանխադրված կարգավորումը"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index fa7b878..379a960 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Tambahkan otomatis"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Telusuri Apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuat Aplikasi..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tidak ditemukan Aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Setelan"</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="accessibility_action_overview" msgid="6257665857640347026">"Ringkasan"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setelan Tampilan Saat Ini tidak memungkinkan putaran"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Badge ikon"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Aktif untuk semua aplikasi"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Nonaktif untuk semua aplikasi"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon ke layar Utama"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk aplikasi baru"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ubah bentuk ikon"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan default sistem"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 1d7a511..cf178a5 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Leita í forritum"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</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="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Stillingar"</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="accessibility_action_overview" msgid="6257665857640347026">"Yfirlit"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Núverandi skjástilling leyfir ekki snúning"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Merking á skjöldum"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Virkt fyrir öll forrit"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Slökkt fyrir öll forrit"</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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Breyta formi tákns"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a63d495..a52aee4 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tieni premuto per posizionare l\'elemento manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Aggiungi automaticamente"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca app"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Caricamento di app…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca nelle app"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Caricamento delle app…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca altre app"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notifiche"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Impostazioni"</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="accessibility_action_overview" msgid="6257665857640347026">"Panoramica"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"L\'impostazione corrente del display non consente la rotazione"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Badge per le icone"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Impostazione attiva per tutte le app"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Impostazione non attiva per tutte le app"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambia la forma delle icone"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Usa impostazione predefinita di sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7f960f2..650a90d 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"חפש אפליקציות"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"טוען אפליקציות…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"הגדרות"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"סקירה"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר הטלפון מסובב"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"הגדרת התצוגה הנוכחית אינה מאפשרת סיבוב"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"הצגת תגים על גבי סמלים"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"מופעלת לכל האפליקציות"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"כבויה לכל האפליקציות"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"הוספת סמל במסך דף הבית"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"שינוי הצורה של הסמלים"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"השתמש בברירת המחדל של המערכת"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 6182eb5..4071a6c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"アプリを検索"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"アプリを読み込んでいます…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</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="out_of_space" msgid="4691004494942118364">"このホーム画面に空きスペースがありません。"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"設定"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"概要"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ホーム画面の回転を許可"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"スマートフォンが回転したとき"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"現在の [ディスプレイ] 設定では回転を使用できません"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"アイコンバッジ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"すべてのアプリで ON"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"すべてのアプリで OFF"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"ホーム画面にアイコンを追加"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"新しいアプリをダウンロードしたときに"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"アイコンの形の変更"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"システムのデフォルトを使用"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7ab510e..7e8b46c 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"აპების ძიება"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</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="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"პარამეტრები"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"მიმოხილვა"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ტელეფონის შეტრიალებისას"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ბრუნვა დაუშვებელია ჩვენების მიმდინარე პარამეტრებით"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ხატულას ბეჯები"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"ჩართულია ყველა აპისთვის"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"გამორთულია ყველა აპისთვის"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ახალი აპებისთვის"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"ხატულას ფორმის შეცვლა"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"ნაგულისხმევი სისტემური პარამეტრების გამოყენება"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index c49ef3b..50ed9aa 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Қолданбаларды іздеу"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Қолданбалар жүктелуде…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» сұрауына сәйкес келетін қолданбалар жоқ"</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="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Параметрлер"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Шолу"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бұрылғанда"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранның ағымдағы параметрі айналуға рұқсат бермейді"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Белгілеу"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Барлық қолданбалар үшін қосулы"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Барлық қолданбалар үшін өшірулі"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңа қолданбаларға арналған"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Белгіше пішінін өзгерту"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Жүйенің әдепкі параметрін пайдалану"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 82bf4bb..7028f7e 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"កំពុងដំណើរការកម្មវិធី..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"គ្មានកម្មវិធីដែលត្រូវជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</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="out_of_space" msgid="4691004494942118364">"គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"ការកំណត់"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់​ទំព័រដើម"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"សង្ខេប"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"នៅពេលដែលបង្វិលទូរស័ព្ទរបស់អ្នក"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ការកំណត់អេក្រង់បច្ចុប្បន្នមិនអនុញ្ញាតការបង្វិលទេ"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ការដាក់ស្លាក​សញ្ញារូបតំណាង"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"បើក​សម្រាប់​កម្មវិធីទាំងអស់"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"បិទសម្រាប់កម្មវិធីទាំងអស់"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"សម្រាប់កម្មវិធីថ្មី"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"ប្តូររូបរាងរូបតំណាង"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"ប្រើលំនាំដើមរបស់ប្រព័ន្ធ"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 6e61af5..9a31649 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</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="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"ಅವಲೋಕನ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್‌ ತಿರುಗಿಸಿದಾಗ"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಸೆಟ್ಟಿಂಗ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ಐಕಾನ್ ಬ್ಯಾಡ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ಆನ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ಆಫ್ ಆಗಿದೆ"</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="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_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>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index d6226d6..e3511d4 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"앱 검색"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"앱 로드 중..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'와(과) 일치하는 앱이 없습니다."</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="out_of_space" msgid="4691004494942118364">"홈 화면에 더 이상 공간이 없습니다."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"설정"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"개요"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"홈 화면 회전 허용"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"휴대전화 회전 시"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"현재 표시 설정에는 회전 기능이 허용되지 않습니다."</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"아이콘 배지"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"모든 앱에서 사용 설정됨"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"모든 앱에서 사용 중지됨"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"홈 화면에 아이콘 추가"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"새로 설치한 앱에 적용"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"아이콘 모양 변경"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"시스템 기본값 사용"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index bdfd15c..85ba8be 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Колдонмолорду издөө"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Колдонмолор жүктөлүүдө…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" дал келген колдонмолор табылган жок"</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="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Жөндөөлөр"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Башкы беттин жөндөөлөрү"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Көз жүгүртүү"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон айланганда"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранды айлантуу параметри өчүрүлгөн"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Бейджик тагуу сүрөтчөсү"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Бардык колдонмолор үчүн күйүк"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Бардык колдонмолор үчүн өчүк"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Сүрөтчөнүн формасын өзгөртүү"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Тутум сушунтаган демейкисин колдонуу"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 3915759..92420a2 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -4,9 +4,9 @@
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
-  
+
           http://www.apache.org/licenses/LICENSE-2.0
-  
+
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,6 +15,39 @@
 -->
 
 <resources>
-<!-- Container -->
-     <item name="container_margin" format="fraction" type="fraction">12%</item>
+    <!-- Container -->
+    <item name="container_margin" format="fraction" type="fraction">12%</item>
+
+    <!-- Fast scroll -->
+    <dimen name="fastscroll_popup_width">58dp</dimen>
+    <dimen name="fastscroll_popup_height">48dp</dimen>
+    <dimen name="fastscroll_popup_padding">10dp</dimen>
+    <dimen name="fastscroll_popup_text_size">24dp</dimen>
+
+    <!-- Dynamic grid -->
+    <dimen name="dynamic_grid_overview_bar_item_width">120dp</dimen>
+    <dimen name="dynamic_grid_min_page_indicator_size">48dp</dimen>
+    <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>
+
+    <!-- Folders -->
+    <dimen name="folder_preview_padding">2dp</dimen>
+
+    <!-- Page indicator -->
+    <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">50dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">74dp</dimen>
+
+    <!-- Hotseat -->
+    <!-- Will be set to equal the hotseat icon size. -->
+    <dimen name="dynamic_grid_hotseat_size">0dp</dimen>
+
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">50dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">44dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
+
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">56dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">32dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 799db3c..9a50c4b 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ຊອກຫາແອັບ"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"​ກຳ​ລັງ​ໂຫລດ​ແອັບ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"ພາບຮວມ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ການຕັ້ງຄ່າສະແດງຜົນປັດຈຸບັນບໍ່ອະນຸຍາດໃຫ້ໝຸນໄດ້"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ປ້າຍໄອຄອນ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"ເປີດສຳລັບທຸກແອັບ"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"ປິດສຳລັບທຸກແອັບ"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ສຳລັບແອັບໃໝ່"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"ປ່ຽນຮູບຮ່າງໄອຄອນ"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index bbf7f1a..d415e4d 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Palieskite ir palaikykite, kad padėtumėte patys"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Pridėti automatiškai"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ieškoti programų"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Įkeliamos programos..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Paieškos programos"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Įkeliamos programos…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Ieškoti daugiau programų"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Nustatymai"</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="accessibility_action_overview" msgid="6257665857640347026">"Apžvalga"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Naudojant dabartinį pateikties nustatymą neleidžiama pasukti"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Piktogramos ženklelių informacija"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Įjungta visoms programoms"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Išjungta visoms programoms"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Pakeisti piktogramos formą"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Naudoti numatytuosius sistemos nustatymus"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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_search" msgid="891119232568284442">"Ieškoti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 75e1bef..2d8bfd8 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Pieskarieties un turiet, lai manuāli pievienotu"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Pievienot automātiski"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Meklēt lietotnes"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Notiek lietotņu ielāde…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne."</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Meklēt lietotnes"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Notiek lietotņu ielāde…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Meklēt citas lietotnes"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Iestatījumi"</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="accessibility_action_overview" msgid="6257665857640347026">"Kopsavilkums"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Pašreizējā displeja iestatījumā nav atļauta pagriešana."</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonu emblēmas"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Ieslēgts visām lietotnēm"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Izslēgts visām lietotnēm"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Mainīt ikonu formu"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Izmantot sistēmas noklusējumu"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index f0c1b64..7d95a23 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Пребарување апликации"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Се вчитуваат апликации…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</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="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Поставки"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Краток преглед"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Кога телефонот се ротира"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Тековната поставка на Екранот не дозволува ротација"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ставање значки на икони"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Вклучено за сите апликации"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Исклучено за сите апликации"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Додајте икона на почетниот екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Променете ја формата на иконата"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Користи ја стандардната поставка на системот"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 9dc4bb1..0cc7aa0 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ആപ്പുകളെ തിരയുക"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പുകൾ ലോഡുചെയ്യുന്നു..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല"</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="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"ക്രമീകരണം"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
-    <string name="accessibility_action_overview" msgid="6257665857640347026">"കാഴ്ച"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"അവലോകനം"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ഐക്കൺ ബാഡ്‌ജിംഗ്"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"എല്ലാ ആപ്പുകൾക്കും ഓണാണ്"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"എല്ലാ ആപ്പുകൾക്കും ഓഫാണ്"</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="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_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>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index c1accc5..22479cc 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Апп хайх"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Аппликейшныг ачаалж байна..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д нийцэх апп олдсонгүй"</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="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Тохиргоо"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Тойм"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Утсыг эргүүлсэн үед"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Дэлгэцийн одоогийн тохиргоогоор эргүүлэх боломжгүй"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Дүрс тэмдэг"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Бүх аппад асаасан"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Бүх аппад унтраасан"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Шинэ аппад зориулсан"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Дүрс тэмдгийн хэлбэрийг өөрчлөх"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Системийн өгөгдмөл тохиргоог ашиглах"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a7a8c80..751ad22 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -27,31 +27,31 @@
     <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="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="7084713969757597256">"अॅप्स शोधा"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"अॅप्स लोड करीत आहे..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</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="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_home_button_label" msgid="252062713717058851">"मुख्‍यपृष्‍ठ"</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="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्‍थापित करा"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"होम सेटिग्ज आणि शॉर्टकट वाचा"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"होम स्क्रीन सेटिंग्ज आणि शॉर्टकट लिहा"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ला फोन कॉल करण्याची अनुमती नाही"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करण्यात समस्या"</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"सेटिंग्ज"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्‍ज"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
-    <string name="accessibility_action_overview" msgid="6257665857640347026">"विहंगावलोकन"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"अवलोकन"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरविला जातो तेव्हा"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"चिन्ह बॅज करणे"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"सर्व अॅपसाठी चालू आहे"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"सर्व अॅपसाठी बंद आहे"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"मुख्य स्क्रीनवर चिन्ह जोडा"</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="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_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>
@@ -92,7 +99,7 @@
     <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_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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 215315a..f94b167 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -27,14 +27,19 @@
     <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="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="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>
@@ -65,12 +70,27 @@
     <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="8119458837558863227">"Tetapan"</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="accessibility_action_overview" msgid="6257665857640347026">"Ikhtisar"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Tetapan Paparan semasa tidak membenarkan putaran"</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="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_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>
@@ -78,6 +98,7 @@
     <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>
@@ -104,4 +125,7 @@
     <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>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index c8bad18..74574b9 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ရှာဖွေမှု အက်ပ်များ"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"အက်ပ်များ ရယူနေစဉ်..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ"</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="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"ဆက်တင်များ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"ခြုံငုံသုံးသပ်ချက်"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"လက်ရှိ မြင်ကွင်းဆက်တင်တွင် မြင်ကွင်းကို လှည့်ခွင့်မပေးပါ"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"သင်္ကေတ တံဆိပ်"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"အက်ပ်များအားလုံးအတွက် ဖွင့်ထားသည်"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"အက်ပ်များအားလုံးအတွက် ပိတ်ထားသည်"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"သင်္ကေတပုံစံကို ပြောင်းရန်"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e75e831..65247c1 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Trykk og hold for å plassere manuelt"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Legg til automatisk"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søk i apper"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Laster inn apper …"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søk etter apper"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Laster inn appene …"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søk etter flere apper"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Varsler"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Innstillinger"</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="accessibility_action_overview" msgid="6257665857640347026">"Oversikt"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Med den nåværende skjerminnstillingen støttes ikke rotasjon"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonmerking"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"På for alle apper"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Av for alle apper"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Legg til ikon på startsiden"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apper"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Endre formen på ikonet"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Bruk systemstandard"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index dee84e6..904dad4 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"अनुप्रयोगहरू लोड गरिँदै..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्"</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="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"सेटिंङहरू"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"गृहपृष्ठका सेटिङहरू"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"परिदृश्य"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"हालको प्रदर्शन सम्बन्धी सेटिङले घुमाउने सुविधालाई अनुमति दिँदैन"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"आइकनलाई पदक दिने कार्य"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"सबै अनुप्रयोगहरूमा सक्रिय छ"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"सबै अनुप्रयोगहरूमा निष्क्रिय छ"</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="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_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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a712d48..7e8def4 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Automatisch toevoegen"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Apps zoeken"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps laden…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</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="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Instellingen"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Instellingen voor homepage"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Overzicht"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige scherminstelling staat draaien niet toe"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Pictogrambadges"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Ingeschakeld voor alle apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Uitgeschakeld voor alle apps"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Pictogram toevoegen aan startscherm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Voor nieuwe apps"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Vorm van pictogram wijzigen"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Systeemstandaard gebruiken"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 8e51bd3..cf5351c 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -25,7 +25,7 @@
     <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_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>
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</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="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
@@ -68,21 +68,28 @@
     <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="settings_button_text" msgid="8119458837558863227">"ਸੈਟਿੰਗਾਂ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"ਰੂਪ-ਰੇਖਾ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ਪ੍ਰਤੀਕ \'ਤੇ ਬੈਜ ਲਗਾਉਣਾ"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇ ਲਈ ਚਾਲੂ ਹੈ"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇ ਲਈ ਬੰਦ ਹੈ"</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="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_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>
@@ -91,7 +98,7 @@
     <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="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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 9192732..c07f7de 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Kliknij i przytrzymaj, by umieścić ręcznie"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatycznie"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Szukaj w aplikacjach"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Wczytuję aplikacje…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Wyszukaj aplikacje"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Ładuję aplikacje…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Wyszukaj więcej aplikacji"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Ustawienia"</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="accessibility_action_overview" msgid="6257665857640347026">"Przegląd"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Obecne ustawienia wyświetlania nie pozwalają na obrót ekranu"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Plakietki na ikonach"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Włączone dla wszystkich aplikacji"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Wyłączone dla wszystkich aplikacji"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Zmień kształt ikon"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Użyj ustawienia domyślnego"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 605dff2..100f07b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Toque sem soltar para colocar manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar aplicações"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"A carregar aplicações..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Não foram encontradas aplic. que correspondam a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar aplicações"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"A carregar aplicações…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma 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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Definições"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Definições da página inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo administrador"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Vista geral"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"A definição de visualização atual não permite a rotação"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Emblemas dos ícones"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Ativada para todas as aplicações"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desativada para todas as aplicações"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma do ícone"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Utilizar a predefinição do sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 7945b07..d75159f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar apps"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Carregando apps…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Configurações"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configurações da página inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Visão geral"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuração atual de exibição não permite rotação"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Uso de selo de ícone"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Ativado para todos os apps"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Desativado para todos os apps"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Adicionar ícone à tela inicial"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novos apps"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma de ícones"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar padrão do sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b433029..512d96d 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Atingeți lung pentru a plasa manual"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Adăugați automat"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Căutați aplicații"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Se încarcă aplicațiile..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Căutați aplicații"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Se încarcă aplicații…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Căutați mai multe aplicații"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Notificări"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Setări"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Setări pentru ecranul de pornire"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Prezentare generală"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setarea actuală a afișajului nu permite rotirea"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Aplicarea insignelor pentru pictograme"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Activată pentru toate aplicațiile"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Dezactivată pentru toate aplicațiile"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Schimbați forma pictogramei"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Folosiți setarea prestabilită a sistemului"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6c87042..923f357 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,16 +28,16 @@
     <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="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="long_accessible_way_to_add" msgid="4289502106628154155">"Чтобы выбрать виджет или использовать специальные действия, нажмите на него дважды и не отпускайте."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Нажмите и удерживайте, чтобы добавить вручную"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Добавить автоматически"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Поиск приложений"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Загрузка…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</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="out_of_space" msgid="4691004494942118364">"На этом экране все занято"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Настройки"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Настройки главного экрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Обзор"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Когда телефон повернут"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"В настройках отключен поворот экрана"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Уведомления на значке приложения"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Включено для всех приложений"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Отключено для всех приложений"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавлять значки"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Добавлять значки установленных приложений на главный экран."</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Изменить форму значков"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Использовать системные настройки по умолчанию"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index f8f8e85..f6c42b2 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"යෙදුම් සෙවීම"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"යෙදුම් පූරණය වෙමින්…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</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="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"සැකසීම්"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"දළ විශ්ලේෂණය"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"දුරකථනය කරකවන විට"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"වත්මන් සංදර්ශක සැකසීම් කරකැවීමට සහාය නොදක්වයි"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"නිරූපක ලාංඡනය"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"සියලු යෙදුම් සඳහා ක්‍රියාත්මකයි"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"සියලු යෙදුම් සඳහා ක්‍රියාවිරහිතයි"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"නව යෙදුම් සඳහා"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"නිරූපක හැඩය වෙනස් කරන්න"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"පද්ධති පෙරනිමි භාවිත කරන්න"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 3d754b2..4dbc407 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Pridať automaticky"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hľadať aplikácie"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítavajú sa aplikácie..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</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="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
@@ -58,7 +58,7 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavenie"</string>
     <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 zakázaná"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je deaktivovaná"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Nastavenia"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia služby Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Prehľad"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuálne nastavenie obrazovky nepovoľuje otáčanie"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Označovanie ikon"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Zapnuté pre všetky aplikácie"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Vypnuté pre všetky aplikácie"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Zmeniť tvar ikony"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Použiť predvolené nastavenie systému"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index fddb445..0b7d36e 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dotaknite se elementa in ga pridržite, da ga ročno dodate"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Samodejno dodaj"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Iskanje po aplikacijah"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Nalaganje aplikacij …"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Iskanje programov"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Nalaganje aplikacij …"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Iskanje več aplikacij"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Nastavitve"</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="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutna nastavitev zaslona ne dovoljuje sukanja"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Označevanje z ikonami"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Vklopljeno za vse aplikacije"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Izklopljeno za vse aplikacije"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Spremeni obliko ikon"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Uporabi privzeto nastavitev sistema"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index f300d03..3e6afee 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Kërko për aplikacione"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Cilësimet"</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="accessibility_action_overview" msgid="6257665857640347026">"Përmbledhje"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cilësimi aktuali i afishimit nuk lejon rrotullimin"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Caktimi i simbolit të ikonës"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Aktiv për të gjitha aplikacionet"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Joaktiv për të gjitha aplikacionet"</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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ndrysho formën e ikonës"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sisteit"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 68cb442..f41e02d 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Претражите апликације"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Апликације се учитавају..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</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="out_of_space" msgid="4691004494942118364">"Нема више простора на овом почетном екрану."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Подешавања"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Преглед"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротацију почетног екрана"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Када се телефон ротира"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Актуелно подешавање приказа не дозвољава ротацију"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Додавање значки на иконе"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Укључено за све апликације"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Искључено за све апликације"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икону на почетни екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нове апликације"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Промените облик икона"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Користи подразумевано системско подешавање"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 4dd6f48..6c598ff 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Placera manuellt genom att trycka länge"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Lägg till automatiskt"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sök efter appar"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Läser in appar …"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Det gick inte att hitta några appar som matchar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sök efter appar"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Läser in appar …"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Inga appar som matchar <xliff:g id="QUERY">%1$s</xliff:g> hittades"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sök efter fler appar"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Inställningar"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Inställningar för startsidan"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Översikt"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Rotering tillåts inte i de nuvarande skärminställningarna"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonmärken"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Aktiverat för alla appar"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Inaktiverat för alla appar"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ändra form på ikoner"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Använd systemstandard"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index ce06bdb..3de28e0 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Gusa na ushikilie ili uweke mwenyewe"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ongeza kiotomatiki"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tafuta Programu"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Inapakia Programu..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Haikupata programu zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tafuta programu"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Inapakia programu..."</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Haikupata programu zozote zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tafuta programu zaidi"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Arifa"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
@@ -72,22 +72,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Mipangilio"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya ukurasa wa mwanzo"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Muhtasari"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mipangilio ya sasa ya sehemu ya Onyesho hairuhusu kuzungusha"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Kuweka beji kwenye aikoni"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Imewashwa katika programu zote"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Imezimwa katika programu zote"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Badilisha umbo la aikoni"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguo-msingi la mfumo"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 358d9b6..b211207 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -15,10 +15,15 @@
 -->
 
 <resources>
-<!-- All Apps -->
+    <!-- All Apps -->
     <dimen name="all_apps_button_scale_down">8dp</dimen>
-    <dimen name="all_apps_search_bar_height">54dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
 
+    <!-- Fast scroll -->
+    <dimen name="fastscroll_popup_width">75dp</dimen>
+    <dimen name="fastscroll_popup_height">62dp</dimen>
+    <dimen name="fastscroll_popup_padding">13dp</dimen>
+    <dimen name="fastscroll_popup_text_size">32dp</dimen>
+
 </resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index de809b1..72894dc 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -26,13 +26,13 @@
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionModeOverlay">true</item>
         <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+        <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
     </style>
 
     <!-- Workspace -->
     <style name="DropTargetButton" parent="DropTargetButtonBase">
         <item name="android:paddingLeft">60dp</item>
         <item name="android:paddingRight">60dp</item>
-        <item name="android:shadowColor">#393939</item>
         <item name="android:shadowDx">0.0</item>
         <item name="android:shadowDy">0.0</item>
         <item name="android:shadowRadius">2.0</item>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index eae471c..1d92581 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</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="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"அமைப்பு"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"மேலோட்டப் பார்வை"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"ஐகான் பேட்ஜிங்"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"எல்லாப் பயன்பாடுகளுக்கும் இயக்கப்பட்டது"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"எல்லாப் பயன்பாடுகளுக்கும் முடக்கப்பட்டது"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"புதிய பயன்பாடுகளுக்கு"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"ஐகான் வடிவத்தை மாற்று"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"அமைப்பின் இயல்புநிலையைப் பயன்படுத்து"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 338c158..6c66699 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/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>
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"అనువర్తనాలను లోడ్ చేస్తోంది…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు"</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="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు."</string>
@@ -56,7 +56,7 @@
     <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>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
@@ -70,25 +70,32 @@
     <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="8119458837558863227">"సెట్టింగ్‌లు"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"హోమ్ సెట్టింగ్‌లు"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"స్థూలదృష్టి"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్‌‌ను తిప్పినప్పుడు"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ప్రస్తుత డిస్‌ప్లే సెట్టింగ్ భ్రమణాన్ని అనుమతించలేదు"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"చిహ్నం బ్యాడ్జ్"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"అన్ని అనువర్తనాలలో ఆన్ చేయబడ్డాయి"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"అన్ని అనువర్తనాలలో ఆఫ్‌ చేయబడ్డాయి"</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="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_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>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 65702a0..b370099 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ค้นหาแอป"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"กำลังโหลดแอป…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"ไม่มีที่ว่างในหน้าจอหลักนี้"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"การตั้งค่า"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"ภาพรวม"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"อนุญาตให้หมุนหน้าจอหลัก"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"เมื่อหมุนโทรศัพท์"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"การตั้งค่าการแสดงผลปัจจุบันไม่อนุญาตให้มีการหมุน"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"การติดป้ายไอคอน"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"เปิดสำหรับแอปทั้งหมด"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"ปิดสำหรับแอปทั้งหมด"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"เพิ่มไอคอนในหน้าจอหลัก"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"สำหรับแอปใหม่"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"เปลี่ยนรูปร่างไอคอน"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"ใช้ค่าเริ่มต้นของระบบ"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 8270708..54135c9 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Awtomatikong idagdag"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Mga App sa Paghahanap"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Nilo-load ang Mga App…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Walang nakitang Mga App na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Mga Setting"</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="accessibility_action_overview" msgid="6257665857640347026">"Pangkalahatang-ideya"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Hindi pinahihintulutan ng kasalukuyang setting ng Display ang pag-rotate"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Pag-badge ng icon"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Naka-on para sa lahat ng app"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Naka-off para sa lahat ng app"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Baguhin ang hugis ng icon"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Gamitin ang default ng system"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index da0aa28..cb8b50a 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Manuel olarak yerleştirmek için dokunun ve basılı tutun"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Otomatik olarak ekle"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Uygulamalarda Ara"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Uygulamalar Yükleniyor…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Uygulamalarda ara"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Uygulamalar yükleniyor…"</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Başka uygulamalar ara"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Ayarlar"</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="accessibility_action_overview" msgid="6257665857640347026">"Genel bakış"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mevcut Ekran ayarı, döndürmeye izin vermiyor"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Simge rozeti ekleme"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Tüm uygulamalar için açık"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Tüm uygulamalar için kapalı"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Ana ekrana simge ekle"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni uygulamalar için"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Simge şeklini değiştir"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem varsayılanını kullan"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a9cefaf..a9e0109 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Пошук додатків"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Завантаження додатків…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</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="out_of_space" msgid="4691004494942118364">"На цьому головному екрані більше немає місця."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Налаштування"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Налаштування Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Огляд"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволити обертання головного екрана"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Коли телефон обертається"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Поточні налаштування дисплея не підтримують обертання"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Позначення значків"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Увімкнено для всіх додатків"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Вимкнено для всіх додатків"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"Додати значок на головний екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для нових додатків"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Змінити форму значка"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Використовувати налаштування системи за умовчанням"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 195a107..a704fab 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"ایپس تلاش کریں"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ایپس لوڈ ہو رہی ہیں…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</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="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
@@ -70,19 +70,26 @@
     <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="8119458837558863227">"ترتیبات"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"مجموعی جائزہ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"جب فون گھمایا جاتا ہے"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"موجودہ ڈسپلے ترتیب گھمانے کی اجازت نہیں دیتی"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"آئیکن کی بیج سازی"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"سبھی ایپس کیلئے آن ہے"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"تمام ایپس کے لئے بند ہے"</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="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_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>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index fbd153b..7e31889 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"Ilovalar ichidan qidirish"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilovalar yuklanmoqda…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” so‘rovi bo‘yicha hech narsa topilmadi"</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="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"Sozlamalar"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Home sozlamalari"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"Umumiy ko‘rinish"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ikonka ustida nishoncha"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Barcha ilovalar uchun yoniq"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Barcha ilovalar uchun o‘chiq"</string>
+    <string name="icon_badging_title" msgid="874121399231955394">"Bildirishnoma nuqtalari"</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 nuqtalarini 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="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka qo‘shish"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikonka shaklini o‘zgartirish"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-v19/styles.xml b/res/values-v19/styles.xml
index cfc7c0f..36c0971 100644
--- a/res/values-v19/styles.xml
+++ b/res/values-v19/styles.xml
@@ -18,7 +18,7 @@
 -->
 <resources>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
         <item name="android:windowTranslucentStatus">true</item>
         <item name="android:windowTranslucentNavigation">true</item>
     </style>
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
index 8d3de01..927719c 100644
--- a/res/values-v21/styles.xml
+++ b/res/values-v21/styles.xml
@@ -18,7 +18,7 @@
 -->
 <resources>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
         <item name="android:windowTranslucentStatus">false</item>
         <item name="android:windowTranslucentNavigation">false</item>
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
index cb18409..b25f46a 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -21,11 +21,9 @@
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
-
-    <!-- From O and above, we show a dark nav bar in all-apps -->
-    <style name="AllAppsNavBarProtection">
-        <item name="android:alpha">0.6</item>
-        <item name="android:background">?android:attr/colorPrimary</item>
+    <style name="WidgetContainerTheme.Dark" parent="LauncherThemeDark">
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+        <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
     </style>
 
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 626ea99..83e1cea 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -35,9 +35,9 @@
     <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="place_automatically" msgid="8064208734425456485">"Tự động thêm"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tìm kiếm ứng dụng"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Đang tải ứng dụng..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"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_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="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
@@ -70,22 +70,27 @@
     <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="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
-    <string name="settings_button_text" msgid="8119458837558863227">"Cài đặt"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt trang chủ"</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="accessibility_action_overview" msgid="6257665857640347026">"Tổng quan"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cài đặt Hiển thị hiện tại không cho phép xoay"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Gắn huy hiệu biểu tượng"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Bật cho tất cả ứng dụng"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Tắt cho tất cả ứng dụng"</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="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="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>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Thay đổi hình dạng biểu tượng"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Sử dụng mặc định của hệ thống"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index fde1986..bef12fb 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"搜索应用"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"正在加载应用…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</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="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"设置"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"概览"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允许旋转主屏幕"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"手机旋转时"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"当前的显示设置不允许旋转设备"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"在图标上显示标记"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"已针对所有应用开启"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"已针对所有应用关闭"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"将图标添加到主屏幕"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"适用于新应用"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"更改图标形状"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系统默认设置"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index d5cf997..7ac5553 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"搜尋應用程式"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"無法找到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</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="out_of_space" msgid="4691004494942118364">"主畫面已無空間。"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"設定"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"概覽"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允許主畫面旋轉"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"「目前顯示屏」設定不允許旋轉"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"在圖示上顯示標誌"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"已為所有應用程式開啟"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"已為所有應用程式關閉"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主畫面"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設設定"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 276ddec..e0c4c99 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -35,9 +35,9 @@
     <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="7084713969757597256">"搜尋應用程式"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"找不到符合「<xliff:g id="QUERY">%1$s</xliff:g>」的應用程式"</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="out_of_space" msgid="4691004494942118364">"這個主螢幕已無空間。"</string>
@@ -70,22 +70,27 @@
     <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="8119458837558863227">"設定"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
     <string name="accessibility_action_overview" msgid="6257665857640347026">"總覽"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"目前的顯示設定不允許旋轉畫面"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"在圖示上顯示標記"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"為所有應用程式開啟這項設定"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"為所有應用程式關閉這項設定"</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="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主螢幕"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設值"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 82fd4cf..ef6fdeb 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -35,9 +35,9 @@
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Thinta futhi ubambe ukuze ubeke ngokwenza"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Engeza ngokuzenzakalelayo"</string>
-    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sesha Izinhlelo Zokusebenza"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilayisha izinhlelo zokusebenza..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+    <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sesha izinhlelo zokusebenza"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"Ilayisha izinhlelo zokusebenza..."</string>
+    <string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sesha izinhlelo zokusebenza eziningi"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
@@ -70,22 +70,27 @@
     <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="settings_button_text" msgid="8119458837558863227">"Izilungiselelo"</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="accessibility_action_overview" msgid="6257665857640347026">"Ukubuka konke"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Isilungiselelo sesiboniso samanje asivumeli ukuzungezisa"</string>
-    <string name="icon_badging_title" msgid="4418239350706629417">"Ibheji yesithonjana"</string>
-    <string name="icon_badging_desc_on" msgid="6022379097717134764">"Kuvuliwe kuzo zonke izinhlelo zokusebenza"</string>
-    <string name="icon_badging_desc_off" msgid="1574640601251590211">"Kuvaliwe kuzo zonke izinhlelo zokusebenza"</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="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="auto_add_shortcuts_label" msgid="8222286205987725611">"Engeza isithonjana eskrinini sasekhaya"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwezinhlelo zokusebenza ezintsha"</string>
-    <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
-    <skip />
+    <string name="icon_shape_override_label" msgid="2977264953998281004">"Shintsha isimo sesithonjana"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Sebenzisa okuzenzakalelayo kwesistimu"</string>
-    <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
-    <skip />
+    <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>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 18f409f..5aee715 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -16,7 +16,22 @@
 */
 -->
 
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Attributes used for launcher theme -->
+    <attr name="allAppsScrimColor" format="color" />
+    <attr name="allAppsNavBarScrimColor" format="color" />
+    <attr name="popupColorPrimary" format="color" />
+    <attr name="popupColorSecondary" format="color" />
+    <attr name="popupColorTertiary" format="color" />
+    <attr name="isMainColorDark" format="boolean" />
+    <attr name="isWorkspaceDarkText" format="boolean" />
+    <attr name="workspaceTextColor" format="color" />
+    <attr name="workspaceShadowColor" format="color" />
+    <attr name="workspaceAmbientShadowColor" format="color"/>
+    <attr name="workspaceKeyShadowColor" format="color" />
+    <attr name="workspaceStatusBarScrim" format="reference" />
+    <attr name="widgetsTheme" format="reference" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
@@ -30,10 +45,17 @@
             <enum name="shortcut_popup" value="4" />
         </attr>
         <attr name="deferShadowGeneration" format="boolean" />
-        <attr name="customShadows" format="boolean" />
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
 
+    <declare-styleable name="ShadowInfo">
+        <attr name="ambientShadowColor" format="color" />
+        <attr name="ambientShadowBlur" format="dimension" />
+        <attr name="keyShadowColor" format="color" />
+        <attr name="keyShadowBlur" format="dimension" />
+        <attr name="keyShadowOffset" format="dimension" />
+    </declare-styleable>
+
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
@@ -96,11 +118,12 @@
         <attr name="numHotseatIcons" format="integer" />
 
         <attr name="iconSize" format="float" />
+        <!-- landscapeIconSize defaults to iconSize, if not specified -->
+        <attr name="landscapeIconSize" format="float" />
         <attr name="iconTextSize" format="float" />
-        <!-- hotseatIconSize defaults to iconSize, if not specified -->
-        <attr name="hotseatIconSize" format="float" />
 
         <attr name="defaultLayoutId" format="reference" />
+        <attr name="demoModeLayoutId" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
@@ -111,4 +134,14 @@
         </attr>
     </declare-styleable>
 
+    <declare-styleable name="ShadowDrawable">
+        <attr name="android:src" />
+        <attr name="android:shadowColor" />
+        <attr name="android:elevation" />
+        <attr name="darkTintColor" format="color"/>
+    </declare-styleable>
+
+    <declare-styleable name="RecyclerViewFastScroller">
+        <attr name="canThumbDetach" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 67a7544..b44a31e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,28 +25,19 @@
 
     <color name="focused_background">#80c6c5c5</color>
 
-    <color name="workspace_icon_text_color">#FFF</color>
-    <color name="workspace_edge_effect_color">#FFFFFFFF</color>
-
     <color name="default_shadow_color_no_alpha">#FF000000</color>
 
-    <color name="outline_color">#FFFFFFFF</color>
-
     <color name="spring_loaded_panel_color">#40FFFFFF</color>
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
     <!-- Popup container -->
-    <color name="popup_header_background_color">#EEEEEE</color> <!-- Gray 200 -->
-    <color name="popup_background_color">#FFF</color>
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
-    <color name="notification_color_beneath">#E0E0E0</color> <!-- Gray 300 -->
-
     <color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
     <color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
 
-    <!-- System shortcuts -->
-    <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color>
-
-    <color name="legacy_icon_background">#FFFFFF</color>
     <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>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 5a8da76..10b612b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,7 +1,7 @@
 <resources>
 <!-- Dynamic Grid -->
     <!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
-    <integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
+    <integer name="config_dynamic_grid_overview_icon_zone_percentage">22</integer>
 
 <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
@@ -9,10 +9,19 @@
     <bool name="is_large_tablet">false</bool>
     <bool name="allow_rotation">false</bool>
 
+    <integer name="extracted_color_gradient_alpha">153</integer>
+
     <!-- A string pointer to the original app name string. This allows derived projects to
      easily override the app name without providing all translations -->
     <string name="derived_app_name" translatable="false">@string/app_name</string>
 
+    <!-- String representing the intent for search on the apps market. To specify a query, add
+    q=<query> to the data to the intent -->
+    <string name="market_search_intent" translatable="false">market://search?c=apps</string>
+
+    <!-- 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">
@@ -26,11 +35,12 @@
     <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>Square</item>
-        <item>Squircle</item>
-        <item>Circle</item>
-        <item>Teardrop</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>
+
 <!-- DragController -->
     <item type="id" name="drag_event_parity" />
 
@@ -59,6 +69,9 @@
     <!-- The duration of the animation from search hint to text entry -->
     <integer name="config_searchHintAnimationDuration">50</integer>
 
+    <!-- View tag key used to store SpringAnimation data. -->
+    <item type="id" name="spring_animation_tag" />
+
 <!-- Workspace -->
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
@@ -76,6 +89,7 @@
     <integer name="config_folderExpandDuration">120</integer>
     <integer name="config_materialFolderExpandDuration">200</integer>
     <integer name="config_materialFolderExpandStagger">60</integer>
+    <integer name="config_folderDelay">30</integer>
 
     <!-- The distance at which the animation should take the max duration -->
     <integer name="config_dropAnimMaxDist">800</integer>
@@ -99,15 +113,18 @@
     <!-- Name of a user event dispatcher class. -->
     <string name="user_event_dispatcher_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>
+
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
 
-    <!-- View ID to use for blocked area on the first screen -->
-    <item type="id" name="workspace_blocked_row" />
-
     <!-- View ID used by cell layout to jail its content -->
     <item type="id" name="cell_layout_jail_id" />
 
@@ -115,11 +132,8 @@
     <item type="id" name="preview_image_id" />
 
 <!-- Popup items -->
-    <integer name="config_deepShortcutOpenDuration">220</integer>
-    <integer name="config_deepShortcutArrowOpenDuration">80</integer>
-    <integer name="config_deepShortcutOpenStagger">40</integer>
-    <integer name="config_deepShortcutCloseDuration">150</integer>
-    <integer name="config_deepShortcutCloseStagger">20</integer>
+    <integer name="config_popupOpenCloseDuration">150</integer>
+    <integer name="config_popupArrowOpenDuration">80</integer>
     <integer name="config_removeNotificationViewDuration">300</integer>
 
 <!-- Accessibility actions -->
@@ -134,4 +148,10 @@
     <item type="id" name="action_resize" />
     <item type="id" name="action_deep_shortcuts" />
     <item type="id" name="action_dismiss_notification" />
+
+<!-- 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" />
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index bd6466b..b1f9d63 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -17,23 +17,37 @@
 <resources>
 <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">8dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_height">28dp</dimen>
+    <dimen name="dynamic_grid_min_page_indicator_size">32dp</dimen>
     <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_gutter_width_left_nav_bar">38dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_gutter_width_right_nav_bar">48dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">0dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">0dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
     <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
     <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
     <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
     <dimen name="dynamic_grid_overview_bar_spacer_width">25dp</dimen>
-    <dimen name="dynamic_grid_hotseat_height">88dp</dimen>
-    <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
-    <dimen name="dynamic_grid_hotseat_gutter_width">24dp</dimen>
-    <dimen name="dynamic_grid_workspace_top_padding">12dp</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 -->
+    <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
+    <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
+    <dimen name="dynamic_grid_hotseat_size">80dp</dimen>
+
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">0dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">0dp</dimen>
+
+
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
     <dimen name="vert_drop_target_vertical_gap">20dp</dimen>
@@ -44,27 +58,31 @@
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
 
-<!-- Container -->
-    <!-- Note: This needs to match the fixed insets for the search box. -->
-    <dimen name="container_bounds_inset">8dp</dimen>
-    <!-- Notes: container_bounds_inset - quantum_panel_outer_padding -->
-    <dimen name="container_bounds_minus_quantum_panel_padding_inset">4dp</dimen>
+<!-- Fast scroll -->
+    <dimen name="fastscroll_track_min_width">6dp</dimen>
+    <dimen name="fastscroll_track_max_width">8dp</dimen>
+    <dimen name="fastscroll_thumb_padding">1dp</dimen>
+    <dimen name="fastscroll_thumb_height">52dp</dimen>
+    <dimen name="fastscroll_thumb_touch_inset">-24dp</dimen>
 
-    <dimen name="container_fastscroll_thumb_min_width">5dp</dimen>
-    <dimen name="container_fastscroll_thumb_max_width">9dp</dimen>
-    <dimen name="container_fastscroll_popup_margin">18dp</dimen>
-    <dimen name="container_fastscroll_thumb_height">72dp</dimen>
-    <dimen name="container_fastscroll_thumb_touch_inset">-24dp</dimen>
-    <dimen name="container_fastscroll_popup_size">72dp</dimen>
-    <dimen name="container_fastscroll_popup_text_size">48dp</dimen>
+    <dimen name="fastscroll_popup_width">75dp</dimen>
+    <dimen name="fastscroll_popup_height">62dp</dimen>
+    <dimen name="fastscroll_popup_padding">13dp</dimen>
+    <dimen name="fastscroll_popup_text_size">32dp</dimen>
+    <dimen name="fastscroll_popup_margin">19dp</dimen>
 
-<!-- All Apps -->
+    <!--
+      Fast scroller draws the content horizontally centered. The end of the track should be
+      aligned at the end of the container.
+        fastscroll_end_margin = - (fastscroll_width - fastscroll_track_min_width) / 2
+    -->
+    <dimen name="fastscroll_width">58dp</dimen>
+    <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_height">60dp</dimen>
-    <dimen name="all_apps_search_bar_icon_margin_right">4dp</dimen>
-    <dimen name="all_apps_search_bar_icon_margin_top">1dp</dimen>
-    <dimen name="all_apps_list_bottom_padding">8dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
@@ -72,7 +90,7 @@
     <dimen name="all_apps_caret_stroke_width">2dp</dimen>
     <dimen name="all_apps_caret_shadow_spread">1dp</dimen>
     <dimen name="all_apps_caret_size">13dp</dimen>
-    <dimen name="all_apps_caret_workspace_offset">4dp</dimen>
+    <dimen name="all_apps_caret_workspace_offset">18dp</dimen>
 
 <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
@@ -109,6 +127,7 @@
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">14dp</dimen>
     <dimen name="drop_target_text_size">14sp</dimen>
+    <dimen name="drop_target_shadow_elevation">2dp</dimen>
 
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
@@ -123,7 +142,7 @@
 
 <!-- Folders -->
     <!-- The size of the padding on the preview background drawable -->
-    <dimen name="folder_preview_padding">6dp</dimen>
+    <dimen name="folder_preview_padding">10dp</dimen>
     <dimen name="page_indicator_dot_size">8dp</dimen>
 
     <dimen name="folder_cell_x_padding">9dp</dimen>
@@ -152,7 +171,7 @@
     <dimen name="deep_shortcuts_elevation">9dp</dimen>
     <dimen name="bg_popup_item_width">220dp</dimen>
     <dimen name="bg_popup_item_height">56dp</dimen>
-    <dimen name="popup_items_spacing">4dp</dimen>
+    <dimen name="bg_popup_item_condensed_height">48dp</dimen>
     <dimen name="pre_drag_view_scale">6dp</dimen>
     <!-- an icon with shortcuts must be dragged this far before the container is removed. -->
     <dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
@@ -195,11 +214,14 @@
     <dimen name="notification_padding_end">12dp</dimen>
     <!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
     <dimen name="notification_footer_icon_row_padding">15dp</dimen>
-    <dimen name="notification_header_height">32dp</dimen>
-    <dimen name="notification_main_height">80dp</dimen>
+    <dimen name="notification_header_height">36dp</dimen>
+    <dimen name="notification_main_height">84dp</dimen>
     <dimen name="notification_footer_height">32dp</dimen>
+    <!-- How much space to keep as padding for the last notification when the footer collapses -->
+    <dimen name="notification_empty_footer_height">6dp</dimen>
     <dimen name="notification_header_text_size">13sp</dimen>
     <dimen name="notification_header_count_text_size">12sp</dimen>
+    <dimen name="notification_main_title_size">16sp</dimen>
     <dimen name="notification_main_text_size">14sp</dimen>
     <dimen name="notification_icon_size">24dp</dimen>
     <dimen name="notification_footer_icon_size">18dp</dimen>
diff --git a/res/drawable/bg_white_round_rect.xml b/res/values/drawables.xml
similarity index 65%
copy from res/drawable/bg_white_round_rect.xml
copy to res/values/drawables.xml
index c7f786f..fea17b1 100644
--- a/res/drawable/bg_white_round_rect.xml
+++ b/res/values/drawables.xml
@@ -5,7 +5,7 @@
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
 
-          http://www.apache.org/licenses/LICENSE-2.0
+        http://www.apache.org/licenses/LICENSE-2.0
 
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,9 +13,8 @@
      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="#FFFFFF" />
-    <corners android:radius="@dimen/bg_round_rect_radius" />
-</shape>
\ No newline at end of file
+<resources>
+    <drawable name="ic_info_shadow">@drawable/ic_info_no_shadow</drawable>
+    <drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
+    <drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ecc537d..1197b1c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -59,11 +59,11 @@
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
-    <string name="all_apps_search_bar_hint">Search Apps</string>
+    <string name="all_apps_search_bar_hint">Search apps</string>
     <!-- Loading apps text. [CHAR_LIMIT=50] -->
-    <string name="all_apps_loading_message">Loading Apps&#8230;</string>
+    <string name="all_apps_loading_message">Loading apps&#8230;</string>
     <!-- No-search-results text. [CHAR_LIMIT=50] -->
-    <string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
+    <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>
 
@@ -159,7 +159,7 @@
     <!-- Text for wallpaper change button -->
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for settings button -->
-    <string name="settings_button_text">Settings</string>
+    <string name="settings_button_text">Home settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
     <string name="msg_disabled_by_admin">Disabled by your admin</string>
     <!-- Text for custom accessibility action to go to the overview mode, where users can look and change the overall UI of the launcher. -->
@@ -178,6 +178,12 @@
     <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>
+    <!-- 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] -->
+    <string name="msg_missing_notification_access">To show Notification Dots, turn on app notifications for <xliff:g id="name" example="My App">%1$s</xliff:g></string>
+    <!-- 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>
 
     <!-- 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>
@@ -188,6 +194,15 @@
     <string name="icon_shape_override_label">Change icon shape</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>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8a46e83..8129e81 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -25,9 +25,62 @@
         <item name="android:windowShowWallpaper">true</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:colorEdgeEffect">#FF757575</item>
+        <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
     </style>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+    <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
+        <item name="allAppsScrimColor">#CCFFFFFF</item>
+        <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
+        <item name="popupColorPrimary">#FFF</item>
+        <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
+        <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+        <item name="isMainColorDark">false</item>
+        <item name="isWorkspaceDarkText">false</item>
+        <item name="workspaceTextColor">@android:color/white</item>
+        <item name="workspaceShadowColor">#B0000000</item>
+        <item name="workspaceAmbientShadowColor">#33000000</item>
+        <item name="workspaceKeyShadowColor">#44000000</item>
+        <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
+        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+    </style>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs"></style>
+
+    <style name="LauncherThemeDarkText" parent="@style/LauncherTheme">
+        <item name="workspaceTextColor">#FF212121</item>
+        <item name="workspaceShadowColor">@android:color/transparent</item>
+        <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
+        <item name="workspaceKeyShadowColor">@android:color/transparent</item>
+        <item name="isWorkspaceDarkText">true</item>
+        <item name="workspaceStatusBarScrim">@null</item>
+    </style>
+
+    <style name="LauncherThemeDark" parent="@style/LauncherTheme">
+        <item name="android:textColorPrimary">#FFFFFFFF</item>
+        <item name="android:textColorSecondary">#FFFFFFFF</item>
+        <item name="android:textColorTertiary">#CCFFFFFF</item>
+        <item name="android:textColorHint">#A0FFFFFF</item>
+        <item name="android:colorControlHighlight">#A0FFFFFF</item>
+        <item name="android:colorPrimary">#FF333333</item>
+        <item name="allAppsScrimColor">#7A212121</item>
+        <item name="allAppsNavBarScrimColor">#80000000</item>
+        <item name="popupColorPrimary">?android:attr/colorPrimary</item>
+        <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
+        <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+        <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+        <item name="isMainColorDark">true</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
+    visible on various wallpapers.
+    -->
+    <style name="HomeScreenElementTheme">
+        <item name="android:colorEdgeEffect">?attr/workspaceTextColor</item>
+        <item name="android:textColorPrimary">?attr/workspaceTextColor</item>
+        <item name="android:textColorSecondary">?attr/workspaceTextColor</item>
+    </style>
 
     <!-- Theme for the widget container. Overridden on API 26. -->
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -36,12 +89,14 @@
         <item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
     </style>
 
+    <style name="WidgetContainerTheme.Dark" />
+
     <style name="FastScrollerPopup" >
-        <item name="android:background">@drawable/container_fastscroll_popup_bg</item>
         <item name="android:layout_width">wrap_content</item>
-        <item name="android:minWidth">@dimen/container_fastscroll_popup_size</item>
-        <item name="android:layout_height">@dimen/container_fastscroll_popup_size</item>
-        <item name="android:textSize">@dimen/container_fastscroll_popup_text_size</item>
+        <item name="android:minWidth">@dimen/fastscroll_popup_width</item>
+        <item name="android:layout_height">@dimen/fastscroll_popup_height</item>
+        <item name="android:textSize">@dimen/fastscroll_popup_text_size</item>
+        <item name="android:paddingEnd">@dimen/fastscroll_popup_padding</item>
         <item name="android:gravity">center</item>
         <item name="android:alpha">0</item>
         <item name="android:elevation">3dp</item>
@@ -50,12 +105,6 @@
         <item name="android:includeFontPadding">false</item>
     </style>
 
-    <!-- Style for nav bar background in all-apps screen -->
-    <style name="AllAppsNavBarProtection">
-        <item name="android:alpha">?android:attr/spotShadowAlpha</item>
-        <item name="android:background">@color/default_shadow_color_no_alpha</item>
-    </style>
-
     <!-- Base theme for BubbleTextView and sub classes -->
     <style name="BaseIcon">
         <item name="android:layout_width">match_parent</item>
@@ -70,15 +119,29 @@
 
         <!-- No shadows in the base theme -->
         <item name="android:shadowRadius">0</item>
-        <item name="customShadows">false</item>
     </style>
 
     <!-- Icon displayed on the worksapce -->
     <style name="BaseIcon.Workspace">
-        <item name="customShadows">true</item>
-        <item name="android:textColor">@color/workspace_icon_text_color</item>
         <item name="android:shadowRadius">2.0</item>
-        <item name="android:shadowColor">#B0000000</item>
+        <item name="android:shadowColor">?attr/workspaceShadowColor</item>
+        <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
+        <item name="ambientShadowBlur">2.5dp</item>
+        <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
+        <item name="keyShadowBlur">1dp</item>
+        <item name="keyShadowOffset">.5dp</item>
+    </style>
+
+    <!-- Theme for the popup container -->
+    <style name="PopupItem">
+        <item name="android:colorControlHighlight">?attr/popupColorTertiary</item>
+    </style>
+    <style name="PopupGutter">
+        <item name="android:backgroundTintMode">multiply</item>
+        <item name="android:backgroundTint">?attr/popupColorSecondary</item>
+        <item name="android:background">@drawable/gutter_horizontal</item>
+        <item name="android:elevation">@dimen/notification_elevation</item>
+        <item name="android:outlineProvider">none</item>
     </style>
 
     <!-- Drop targets -->
@@ -86,15 +149,30 @@
         <item name="android:drawablePadding">7.5dp</item>
         <item name="android:paddingLeft">16dp</item>
         <item name="android:paddingRight">16dp</item>
-        <item name="android:textColor">@color/workspace_icon_text_color</item>
+        <item name="android:textColor">?attr/workspaceTextColor</item>
         <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">end</item>
-        <item name="android:shadowColor">@color/default_shadow_color_no_alpha</item>
+        <item name="android:shadowColor">?attr/workspaceShadowColor</item>
         <item name="android:shadowDx">0.0</item>
         <item name="android:shadowDy">1.0</item>
         <item name="android:shadowRadius">4.0</item>
     </style>
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
+
+    <style name="TextTitle">
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+
+    <style name="AllAppsEmptySearchBackground">
+        <item name="android:colorPrimary">#E0E0E0</item>
+        <item name="android:colorControlHighlight">#BDBDBD</item>
+        <item name="android:colorForeground">@color/all_apps_bg_hand_fill</item>
+    </style>
+    <style name="AllAppsEmptySearchBackground.Dark">
+        <item name="android:colorPrimary">#9AA0A6</item>
+        <item name="android:colorControlHighlight">#DFE1E5</item>
+        <item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
+    </style>
 </resources>
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 7e833a0..299e92e 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -3,5 +3,6 @@
 
     <include domain="database" path="launcher.db" />
     <include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
+    <include domain="file" path="downgrade_schema.json" />
 
 </full-backup-content>
\ No newline at end of file
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index aeda1a2..c582fc5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -29,7 +29,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="3"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_3x3"
         />
 
@@ -45,7 +44,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="3"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_3x3"
         />
 
@@ -61,7 +59,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -77,7 +74,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -93,7 +89,6 @@
         launcher:iconSize="48"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="48"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -106,10 +101,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="60"
+        launcher:iconSize="54"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -122,10 +116,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="60"
+        launcher:iconSize="54"
         launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
 
@@ -138,10 +131,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="64"
+        launcher:iconSize="56"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="5"
-        launcher:hotseatIconSize="56"
         launcher:defaultLayoutId="@xml/default_workspace_5x5"
         />
 
@@ -154,10 +146,9 @@
         launcher:numFolderRows="4"
         launcher:numFolderColumns="5"
         launcher:minAllAppsPredictionColumns="4"
-        launcher:iconSize="72"
+        launcher:iconSize="64"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="60"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
@@ -173,7 +164,6 @@
         launcher:iconSize="76"
         launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="76"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
@@ -189,7 +179,6 @@
         launcher:iconSize="100"
         launcher:iconTextSize="20.0"
         launcher:numHotseatIcons="7"
-        launcher:hotseatIconSize="72"
         launcher:defaultLayoutId="@xml/default_workspace_5x6"
         />
 
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 8763883..28a35b8 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -16,6 +16,19 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <com.android.launcher3.views.ButtonPreference
+        android:key="pref_icon_badging"
+        android:title="@string/icon_badging_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 -->
+            <extra
+                android:name=":settings:fragment_args_key"
+                android:value="notification_badging" />
+        </intent>
+    </com.android.launcher3.views.ButtonPreference>
+
     <SwitchPreference
         android:key="pref_add_icon_to_home"
         android:title="@string/auto_add_shortcuts_label"
@@ -24,6 +37,13 @@
         android:persistent="true"
         />
 
+    <SwitchPreference
+        android:key="pref_allowRotation"
+        android:title="@string/allow_rotation_title"
+        android:defaultValue="@bool/allow_rotation"
+        android:persistent="true"
+        />
+
     <ListPreference
         android:key="pref_override_icon_shape"
         android:title="@string/icon_shape_override_label"
@@ -33,23 +53,4 @@
         android:defaultValue=""
         android:persistent="false" />
 
-    <Preference
-        android:key="pref_icon_badging"
-        android:title="@string/icon_badging_title"
-        android:persistent="false">
-        <intent android:action="android.settings.NOTIFICATION_SETTINGS">
-            <!-- This extra highlights the "Allow icon badges" field in Notification settings -->
-            <extra
-                android:name=":settings:fragment_args_key"
-                android:value="notification_badging" />
-        </intent>
-    </Preference>/>
-
-    <SwitchPreference
-        android:key="pref_allowRotation"
-        android:title="@string/allow_rotation_title"
-        android:defaultValue="@bool/allow_rotation"
-        android:persistent="true"
-        />
-
 </PreferenceScreen>
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 5b42cad..8ac8570 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -18,10 +18,16 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 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.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
 
@@ -34,18 +40,18 @@
  * Stores the list of all applications for the all apps view.
  */
 public class AllAppsList {
+    private static final String TAG = "AllAppsList";
+
     public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
 
     /** The list off all apps. */
-    public ArrayList<AppInfo> data =
-            new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
+    public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
     /** The list of apps that have been added since the last notify() call. */
-    public ArrayList<AppInfo> added =
-            new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
+    public ArrayList<AppInfo> added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
     /** The list of apps that have been removed since the last notify() call. */
-    public ArrayList<AppInfo> removed = new ArrayList<AppInfo>();
+    public ArrayList<AppInfo> removed = new ArrayList<>();
     /** The list of apps that have been modified since the last notify() call. */
-    public ArrayList<AppInfo> modified = new ArrayList<AppInfo>();
+    public ArrayList<AppInfo> modified = new ArrayList<>();
 
     private IconCache mIconCache;
 
@@ -69,7 +75,7 @@
         if (!mAppFilter.shouldShowApp(info.componentName)) {
             return;
         }
-        if (findActivity(data, info.componentName, info.user)) {
+        if (findAppInfo(info.componentName, info.user) != null) {
             return;
         }
         mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
@@ -78,6 +84,25 @@
         added.add(info);
     }
 
+    public void addPromiseApp(Context context,
+                              PackageInstallerCompat.PackageInstallInfo installInfo) {
+        ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
+                .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
+        // only if not yet installed
+        if (applicationInfo == null) {
+            PromiseAppInfo info = new PromiseAppInfo(installInfo);
+            mIconCache.getTitleAndIcon(info, info.usingLowResIcon);
+            data.add(info);
+            added.add(info);
+        }
+    }
+
+    public void removePromiseApp(AppInfo appInfo) {
+        // the <em>removed</em> list is handled by the caller
+        // so not adding it here
+        data.remove(appInfo);
+    }
+
     public void clear() {
         data.clear();
         // TODO: do we clear these too?
@@ -160,6 +185,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.");
                         removed.add(applicationInfo);
                         data.remove(i);
                     }
@@ -169,9 +195,7 @@
             // Find enabled activities and add them to the adapter
             // Also updates existing activities with new labels/icons
             for (final LauncherActivityInfo info : matches) {
-                AppInfo applicationInfo = findApplicationInfoLocked(
-                        info.getComponentName().getPackageName(), user,
-                        info.getComponentName().getClassName());
+                AppInfo applicationInfo = findAppInfo(info.getComponentName(), user);
                 if (applicationInfo == null) {
                     add(new AppInfo(context, info, user), info);
                 } else {
@@ -208,28 +232,14 @@
     }
 
     /**
-     * Returns whether <em>apps</em> contains <em>component</em>.
+     * Find an AppInfo object for the given componentName
+     *
+     * @return the corresponding AppInfo or null
      */
-    private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
-            UserHandle user) {
-        final int N = apps.size();
-        for (int i = 0; i < N; i++) {
-            final AppInfo info = apps.get(i);
-            if (info.user.equals(user) && info.componentName.equals(component)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Find an ApplicationInfo object for the given packageName and className.
-     */
-    private AppInfo findApplicationInfoLocked(String packageName, UserHandle user,
-            String className) {
+    private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
+                                          @NonNull UserHandle user) {
         for (AppInfo info: data) {
-            if (user.equals(info.user) && packageName.equals(info.componentName.getPackageName())
-                    && className.equals(info.componentName.getClassName())) {
+            if (componentName.equals(info.componentName) && user.equals(info.user)) {
                 return info;
             }
         }
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index db8f5dd..923835a 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -1,9 +1,14 @@
 package com.android.launcher3;
 
 import android.content.ComponentName;
+import android.content.Context;
 
 public class AppFilter {
 
+    public static AppFilter newInstance(Context context) {
+        return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+    }
+
     public boolean shouldShowApp(ComponentName app) {
         return true;
     }
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9ec26e2..7d2f753 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
@@ -31,6 +32,10 @@
  */
 public class AppInfo extends ItemInfoWithIcon {
 
+    public static final int FLAG_SYSTEM_UNKNOWN = 0;
+    public static final int FLAG_SYSTEM_YES = 1 << 0;
+    public static final int FLAG_SYSTEM_NO = 1 << 1;
+
     /**
      * The intent used to start the application.
      */
@@ -43,6 +48,11 @@
      */
     public int isDisabled = ShortcutInfo.DEFAULT;
 
+    /**
+     * Stores if the app is a system app or not.
+     */
+    public int isSystemApp;
+
     public AppInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
     }
@@ -71,6 +81,10 @@
         }
 
         intent = makeLaunchIntent(info);
+
+        isSystemApp = (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
+
     }
 
     public AppInfo(AppInfo info) {
@@ -79,6 +93,7 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         isDisabled = info.isDisabled;
+        isSystemApp = info.isSystemApp;
     }
 
     @Override
@@ -95,10 +110,14 @@
     }
 
     public static Intent makeLaunchIntent(LauncherActivityInfo info) {
+        return makeLaunchIntent(info.getComponentName());
+    }
+
+    public static Intent makeLaunchIntent(ComponentName cn) {
         return new Intent(Intent.ACTION_MAIN)
-            .addCategory(Intent.CATEGORY_LAUNCHER)
-            .setComponent(info.getComponentName())
-            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setComponent(cn)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
     @Override
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1a405f9..a486a3a 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -55,7 +55,6 @@
 
     private final int[] mDirectionVector = new int[2];
     private final int[] mLastDirectionVector = new int[2];
-    private final int[] mTmpPt = new int[2];
 
     private final IntRange mTempRange1 = new IntRange();
     private final IntRange mTempRange2 = new IntRange();
@@ -344,13 +343,12 @@
         return rect;
     }
 
-    /**
-     * This is the final step of the resize. Here we save the new widget size and position
-     * to LauncherModel and animate the resize frame.
-     */
-    public void commitResize() {
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        // We are done with resizing the widget. Save the widget size & position to LauncherModel
         resizeWidgetIfNeeded(true);
-        requestLayout();
     }
 
     private void onTouchUp() {
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 84a8bce..b249c95 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -9,9 +9,13 @@
 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;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ContentWriter;
 
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -21,6 +25,12 @@
     @Override
     public void onReceive(final Context context, Intent intent) {
         if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
+            int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0);
+            Log.d(TAG, "Widget ID map received for host:" + hostId);
+            if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) {
+                return;
+            }
+
             final int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
             final int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
             if (oldIds.length == newIds.length) {
@@ -29,7 +39,8 @@
                         .postAtFrontOfQueue(new Runnable() {
                             @Override
                             public void run() {
-                                restoreAppWidgetIds(context, asyncResult, oldIds, newIds);
+                                restoreAppWidgetIds(context, oldIds, newIds);
+                                asyncResult.finish();
                             }
                         });
             } else {
@@ -41,18 +52,33 @@
     /**
      * Updates the app widgets whose id has changed during the restore process.
      */
-    static void restoreAppWidgetIds(Context context, PendingResult asyncResult,
-            int[] oldWidgetIds, int[] newWidgetIds) {
+    @WorkerThread
+    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");
+            appWidgetHost.deleteHost();
+            return;
+        }
+        if (!RestoreDbTask.isPending(context)) {
+            // Someone has already gone through our DB once, probably LoaderTask. Skip any further
+            // modifications of the DB.
+            Log.e(TAG, "Skipping widget ID remap as DB already in use");
+            for (int widgetId : newWidgetIds) {
+                Log.d(TAG, "Deleting widgetId: " + widgetId);
+                appWidgetHost.deleteAppWidgetId(widgetId);
+            }
+            return;
+        }
         final ContentResolver cr = context.getContentResolver();
         final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
-        AppWidgetHost appWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
 
         for (int i = 0; i < oldWidgetIds.length; i++) {
             Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
 
             final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
             final int state;
-            if (LauncherModel.isValidProvider(provider)) {
+            if (LoaderTask.isValidProvider(provider)) {
                 // This will ensure that we show 'Click to setup' UI if required.
                 state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
             } else {
@@ -85,6 +111,5 @@
         if (app != null) {
             app.getModel().forceReload();
         }
-        asyncResult.finish();
     }
 }
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index c4086a8..d82579b 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -30,23 +30,20 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Patterns;
-
 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.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Locale;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 /**
  * Layout parsing code for auto installs layout
@@ -83,7 +80,7 @@
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
-                (int) grid.numColumns, (int) grid.numRows, (int) grid.numHotseatIcons);
+            grid.numColumns, grid.numRows, grid.numHotseatIcons);
         int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
 
         // Try with only grid size
@@ -91,7 +88,7 @@
             Log.d(TAG, "Formatted layout: " + layoutName
                     + " not found. Trying layout without hosteat");
             layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES,
-                    (int) grid.numColumns, (int) grid.numRows);
+                grid.numColumns, grid.numRows);
             layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
         }
 
@@ -209,7 +206,7 @@
         beginDocument(parser, mRootTag);
         final int depth = parser.getDepth();
         int type;
-        HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
+        ArrayMap<String, TagParser> tagParserMap = getLayoutElementsMap();
         int count = 0;
 
         while (((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -243,10 +240,10 @@
      * Parses the current node and returns the number of elements added.
      */
     protected int parseAndAddNode(
-            XmlResourceParser parser,
-            HashMap<String, TagParser> tagParserMap,
-            ArrayList<Long> screenIds)
-                    throws XmlPullParserException, IOException {
+        XmlResourceParser parser,
+        ArrayMap<String, TagParser> tagParserMap,
+        ArrayList<Long> screenIds)
+        throws XmlPullParserException, IOException {
 
         if (TAG_INCLUDE.equals(parser.getName())) {
             final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
@@ -303,16 +300,16 @@
         }
     }
 
-    protected HashMap<String, TagParser> getFolderElementsMap() {
-        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+    protected ArrayMap<String, TagParser> getFolderElementsMap() {
+        ArrayMap<String, TagParser> parsers = new ArrayMap<>();
         parsers.put(TAG_APP_ICON, new AppShortcutParser());
         parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
         parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
         return parsers;
     }
 
-    protected HashMap<String, TagParser> getLayoutElementsMap() {
-        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+    protected ArrayMap<String, TagParser> getLayoutElementsMap() {
+        ArrayMap<String, TagParser> parsers = new ArrayMap<>();
         parsers.put(TAG_APP_ICON, new AppShortcutParser());
         parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
         parsers.put(TAG_FOLDER, new FolderParser());
@@ -393,7 +390,7 @@
                 return -1;
             }
 
-            mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINTALL_ICON);
+            mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINSTALL_ICON);
             final Intent intent = new Intent(Intent.ACTION_MAIN, null)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setComponent(new ComponentName(packageName, className))
@@ -528,13 +525,13 @@
     }
 
     protected class FolderParser implements TagParser {
-        private final HashMap<String, TagParser> mFolderElements;
+        private final ArrayMap<String, TagParser> mFolderElements;
 
         public FolderParser() {
             this(getFolderElementsMap());
         }
 
-        public FolderParser(HashMap<String, TagParser> elements) {
+        public FolderParser(ArrayMap<String, TagParser> elements) {
             mFolderElements = elements;
         }
 
@@ -561,7 +558,7 @@
             }
 
             final ContentValues myValues = new ContentValues(mValues);
-            ArrayList<Long> folderItems = new ArrayList<Long>();
+            ArrayList<Long> folderItems = new ArrayList<>();
 
             int type;
             int folderDepth = parser.getDepth();
@@ -617,7 +614,7 @@
         }
     }
 
-    protected static final void beginDocument(XmlPullParser parser, String firstElementName)
+    protected static void beginDocument(XmlPullParser parser, String firstElementName)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -671,7 +668,7 @@
         return value;
     }
 
-    public static interface LayoutParserCallback {
+    public interface LayoutParserCallback {
         long generateNewItemId();
 
         long insertAndCheck(SQLiteDatabase db, ContentValues values);
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 08cd955..e496495 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -19,14 +19,17 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.util.SystemUiController;
 
 public abstract class BaseActivity extends Activity {
 
     protected DeviceProfile mDeviceProfile;
     protected UserEventDispatcher mUserEventDispatcher;
+    protected SystemUiController mSystemUiController;
 
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -54,4 +57,16 @@
         }
         return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
     }
+
+    public SystemUiController getSystemUiController() {
+        if (mSystemUiController == null) {
+            mSystemUiController = new SystemUiController(getWindow());
+        }
+        return mSystemUiController;
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+    }
 }
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index ac7cbaf..82175b7 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -62,7 +62,7 @@
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
+        if (this instanceof AllAppsContainerView) {
             mBaseDrawable = new ColorDrawable();
         } else {
             TypedArray a = context.obtainStyledAttributes(attrs,
@@ -113,20 +113,18 @@
      * Calculate the background padding as it can change due to insets/content padding change.
      */
     private void updatePaddings() {
-        Context context = getContext();
-        int paddingLeft;
-        int paddingRight;
-        int paddingTop;
-        int paddingBottom;
-
-        DeviceProfile grid = Launcher.getLauncher(context).getDeviceProfile();
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
         int[] padding = grid.getContainerPadding();
-        paddingLeft = padding[0] + grid.edgeMarginPx;
-        paddingRight = padding[1] + grid.edgeMarginPx;
+
+        int paddingLeft = padding[0];
+        int paddingRight = padding[1];
+        int paddingTop = 0;
+        int paddingBottom = 0;
+
         if (!grid.isVerticalBarLayout()) {
+            paddingLeft += grid.edgeMarginPx;
+            paddingRight += grid.edgeMarginPx;
             paddingTop = paddingBottom = grid.edgeMarginPx;
-        } else {
-            paddingTop = paddingBottom = 0;
         }
         updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
     }
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 6fdf454..3ee6e51 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,8 +22,9 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.ViewGroup;
+import android.widget.TextView;
 
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.RecyclerViewFastScroller;
 
 
 /**
@@ -36,17 +37,7 @@
 public abstract class BaseRecyclerView extends RecyclerView
         implements RecyclerView.OnItemTouchListener {
 
-    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
-
-    /** Keeps the last known scrolling delta/velocity along y-axis. */
-    @Thunk int mDy = 0;
-    private float mDeltaThreshold;
-
-    protected final BaseRecyclerViewFastScrollBar mScrollbar;
-
-    private int mDownX;
-    private int mDownY;
-    private int mLastY;
+    protected RecyclerViewFastScroller mScrollbar;
 
     public BaseRecyclerView(Context context) {
         this(context, null);
@@ -58,32 +49,6 @@
 
     public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
-        mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
-
-        ScrollListener listener = new ScrollListener();
-        setOnScrollListener(listener);
-    }
-
-    private class ScrollListener extends OnScrollListener {
-        public ScrollListener() {
-            // Do nothing
-        }
-
-        @Override
-        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-            mDy = dy;
-
-            // TODO(winsonc): If we want to animate the section heads while scrolling, we can
-            //                initiate that here if the recycler view scroll state is not
-            //                RecyclerView.SCROLL_STATE_IDLE.
-
-            onUpdateScrollbar(dy);
-        }
-    }
-
-    public void reset() {
-        mScrollbar.reattachThumbToScroll();
     }
 
     @Override
@@ -95,7 +60,9 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup));
+        ViewGroup parent = (ViewGroup) getParent();
+        mScrollbar = parent.findViewById(R.id.fast_scroller);
+        mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup));
     }
 
     /**
@@ -117,30 +84,15 @@
      * it is already showing).
      */
     private boolean handleTouchEvent(MotionEvent ev) {
-        int action = ev.getAction();
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                // Keep track of the down positions
-                mDownX = x;
-                mDownY = mLastY = y;
-                if (shouldStopScroll(ev)) {
-                    stopScroll();
-                }
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
-            case MotionEvent.ACTION_MOVE:
-                mLastY = y;
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                onFastScrollCompleted();
-                mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
-                break;
+        // Move to mScrollbar's coordinate system.
+        int left = getLeft() - mScrollbar.getLeft();
+        int top = getTop() - mScrollbar.getTop();
+        ev.offsetLocation(left, top);
+        try {
+            return mScrollbar.handleTouchEvent(ev);
+        } finally {
+            ev.offsetLocation(-left, -top);
         }
-        return mScrollbar.isDraggingThumb();
     }
 
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -148,25 +100,10 @@
     }
 
     /**
-     * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
-     */
-    protected boolean shouldStopScroll(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if ((Math.abs(mDy) < mDeltaThreshold &&
-                    getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
-                // now the touch events are being passed to the {@link WidgetCell} until the
-                // touch sequence goes over the touch slop.
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Returns the height of the fast scroll bar
      */
-    protected int getScrollbarTrackHeight() {
-        return getHeight();
+    public int getScrollbarTrackHeight() {
+        return getHeight() - getPaddingTop() - getPaddingBottom();
     }
 
     /**
@@ -185,24 +122,16 @@
     }
 
     /**
-     * Returns the track color (ignoring alpha), can be overridden by each subclass.
-     */
-    public int getFastScrollerTrackColor(int defaultTrackColor) {
-        return defaultTrackColor;
-    }
-
-    /**
      * Returns the scrollbar for this recycler view.
      */
-    public BaseRecyclerViewFastScrollBar getScrollBar() {
+    public RecyclerViewFastScroller getScrollBar() {
         return mScrollbar;
     }
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
         onUpdateScrollbar(0);
-        mScrollbar.draw(canvas);
+        super.dispatchDraw(canvas);
     }
 
     /**
@@ -233,7 +162,7 @@
     /**
      * @return whether fast scrolling is supported in the current state.
      */
-    protected boolean supportsFastScrolling() {
+    public boolean supportsFastScrolling() {
         return true;
     }
 
@@ -249,16 +178,16 @@
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      * <p>Override in each subclass of this base class.
      */
-    protected abstract String scrollToPositionAtProgress(float touchFraction);
+    public abstract String scrollToPositionAtProgress(float touchFraction);
 
     /**
      * Updates the bounds for the scrollbar.
      * <p>Override in each subclass of this base class.
      */
-    protected abstract void onUpdateScrollbar(int dy);
+    public abstract void onUpdateScrollbar(int dy);
 
     /**
      * <p>Override in each subclass of this base class.
      */
-    protected void onFastScrollCompleted() {}
+    public void onFastScrollCompleted() {}
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
deleted file mode 100644
index 5feb42e..0000000
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.TextView;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
-
-/**
- * The track and scrollbar that shows when you scroll the list.
- */
-public class BaseRecyclerViewFastScrollBar {
-
-    private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
-            new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
-
-                @Override
-                public Integer get(BaseRecyclerViewFastScrollBar scrollBar) {
-                    return scrollBar.mWidth;
-                }
-
-                @Override
-                public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) {
-                    scrollBar.setTrackWidth(value);
-                }
-            };
-
-    private final static int MAX_TRACK_ALPHA = 30;
-    private final static int SCROLL_BAR_VIS_DURATION = 150;
-    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
-
-    private final Rect mTmpRect = new Rect();
-    private final BaseRecyclerView mRv;
-
-    private final boolean mIsRtl;
-
-    // The inset is the buffer around which a point will still register as a click on the scrollbar
-    private final int mTouchInset;
-
-    private final int mMinWidth;
-    private final int mMaxWidth;
-
-    // Current width of the track
-    private int mWidth;
-    private ObjectAnimator mWidthAnimator;
-
-    private final Path mThumbPath = new Path();
-    private final Paint mThumbPaint;
-    private final int mThumbHeight;
-
-    private final Paint mTrackPaint;
-
-    private float mLastTouchY;
-    private boolean mIsDragging;
-    private boolean mIsThumbDetached;
-    private boolean mCanThumbDetach;
-    private boolean mIgnoreDragGesture;
-
-    // This is the offset from the top of the scrollbar when the user first starts touching.  To
-    // prevent jumping, this offset is applied as the user scrolls.
-    private int mTouchOffsetY;
-    private int mThumbOffsetY;
-
-    // Fast scroller popup
-    private TextView mPopupView;
-    private boolean mPopupVisible;
-    private String mPopupSectionName;
-
-    public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
-        mRv = rv;
-        mTrackPaint = new Paint();
-        mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
-        mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
-
-        mThumbPaint = new Paint();
-        mThumbPaint.setAntiAlias(true);
-        mThumbPaint.setColor(Themes.getColorAccent(rv.getContext()));
-        mThumbPaint.setStyle(Paint.Style.FILL);
-
-        mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width);
-        mMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width);
-        mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height);
-        mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset);
-        mIsRtl = Utilities.isRtl(res);
-        updateThumbPath();
-    }
-
-    public void setPopupView(View popup) {
-        mPopupView = (TextView) popup;
-    }
-
-    public void setDetachThumbOnFastScroll() {
-        mCanThumbDetach = true;
-    }
-
-    public void reattachThumbToScroll() {
-        mIsThumbDetached = false;
-    }
-
-    private int getDrawLeft() {
-        return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth);
-    }
-
-    public void setThumbOffsetY(int y) {
-        if (mThumbOffsetY == y) {
-            return;
-        }
-
-        // Invalidate the previous and new thumb area
-        int drawLeft = getDrawLeft();
-        mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mThumbOffsetY = y;
-        mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mRv.invalidate(mTmpRect);
-    }
-
-    public int getThumbOffsetY() {
-        return mThumbOffsetY;
-    }
-
-    private void setTrackWidth(int width) {
-        if (mWidth == width) {
-            return;
-        }
-        int left = getDrawLeft();
-        // Invalidate the whole scroll bar area.
-        mRv.invalidate(left, 0, left + mMaxWidth, mRv.getScrollbarTrackHeight());
-
-        mWidth = width;
-        updateThumbPath();
-    }
-
-    /**
-     * Updates the path for the thumb drawable.
-     */
-    private void updateThumbPath() {
-        int smallWidth = mIsRtl ? mWidth : -mWidth;
-        int largeWidth = mIsRtl ? mMaxWidth : -mMaxWidth;
-
-        mThumbPath.reset();
-        mThumbPath.moveTo(0, 0);
-        mThumbPath.lineTo(0, mThumbHeight);             // Left edge
-        mThumbPath.lineTo(smallWidth, mThumbHeight);    // bottom edge
-        mThumbPath.cubicTo(smallWidth, mThumbHeight,    // right edge
-                largeWidth, mThumbHeight / 2,
-                smallWidth, 0);
-        mThumbPath.close();
-    }
-
-    public int getThumbHeight() {
-        return mThumbHeight;
-    }
-
-    public boolean isDraggingThumb() {
-        return mIsDragging;
-    }
-
-    public boolean isThumbDetached() {
-        return mIsThumbDetached;
-    }
-
-    /**
-     * Handles the touch event and determines whether to show the fast scroller (or updates it if
-     * it is already showing).
-     */
-    public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) {
-        ViewConfiguration config = ViewConfiguration.get(mRv.getContext());
-
-        int action = ev.getAction();
-        int y = (int) ev.getY();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                if (isNearThumb(downX, downY)) {
-                    mTouchOffsetY = downY - mThumbOffsetY;
-                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
-                        && mRv.supportsFastScrolling()
-                        && isNearScrollBar(downX)) {
-                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
-                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
-                // exceeded some fixed movement
-                mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop();
-                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
-                        isNearThumb(downX, lastY) &&
-                        Math.abs(y - downY) > config.getScaledTouchSlop()) {
-                    calcTouchOffsetAndPrepToFastScroll(downY, lastY);
-                }
-                if (mIsDragging) {
-                    updateFastScrollSectionNameAndThumbOffset(lastY, y);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mTouchOffsetY = 0;
-                mLastTouchY = 0;
-                mIgnoreDragGesture = false;
-                if (mIsDragging) {
-                    mIsDragging = false;
-                    animatePopupVisibility(false);
-                    showActiveScrollbar(false);
-                }
-                break;
-        }
-    }
-
-    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
-        mRv.getParent().requestDisallowInterceptTouchEvent(true);
-        mIsDragging = true;
-        if (mCanThumbDetach) {
-            mIsThumbDetached = true;
-        }
-        mTouchOffsetY += (lastY - downY);
-        animatePopupVisibility(true);
-        showActiveScrollbar(true);
-    }
-
-    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
-        // Update the fastscroller section name at this touch position
-        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
-        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
-        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
-        if (!sectionName.equals(mPopupSectionName)) {
-            mPopupSectionName = sectionName;
-            mPopupView.setText(sectionName);
-        }
-        animatePopupVisibility(!sectionName.isEmpty());
-        updatePopupY(lastY);
-        mLastTouchY = boundedY;
-        setThumbOffsetY((int) mLastTouchY);
-    }
-
-    public void draw(Canvas canvas) {
-        if (mThumbOffsetY < 0) {
-            return;
-        }
-        int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        if (!mIsRtl) {
-            canvas.translate(mRv.getWidth(), 0);
-        }
-        // Draw the track
-        int thumbWidth = mIsRtl ? mWidth : -mWidth;
-        canvas.drawRect(0, 0, thumbWidth, mRv.getScrollbarTrackHeight(), mTrackPaint);
-
-        canvas.translate(0, mThumbOffsetY);
-        canvas.drawPath(mThumbPath, mThumbPaint);
-        canvas.restoreToCount(saveCount);
-    }
-
-    /**
-     * Animates the width of the scrollbar.
-     */
-    private void showActiveScrollbar(boolean isScrolling) {
-        if (mWidthAnimator != null) {
-            mWidthAnimator.cancel();
-        }
-
-        mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
-                isScrolling ? mMaxWidth : mMinWidth);
-        mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
-        mWidthAnimator.start();
-    }
-
-    /**
-     * Returns whether the specified point is inside the thumb bounds.
-     */
-    public boolean isNearThumb(int x, int y) {
-        int left = getDrawLeft();
-        mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight);
-        mTmpRect.inset(mTouchInset, mTouchInset);
-        return mTmpRect.contains(x, y);
-    }
-
-    /**
-     * Returns whether the specified x position is near the scroll bar.
-     */
-    public boolean isNearScrollBar(int x) {
-        int left = getDrawLeft();
-        return x >= left && x <= left + mMaxWidth;
-    }
-
-    private void animatePopupVisibility(boolean visible) {
-        if (mPopupVisible != visible) {
-            mPopupVisible = visible;
-            mPopupView.animate().cancel();
-            mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
-        }
-    }
-
-    private void updatePopupY(int lastTouchY) {
-        int height = mPopupView.getHeight();
-        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height);
-        top = Math.max(mMaxWidth, Math.min(top, mRv.getScrollbarTrackHeight() - mMaxWidth - height));
-        mPopupView.setTranslationY(top);
-    }
-}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3c3e224..ac842f9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,15 +19,16 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.util.TypedValue;
@@ -44,6 +45,7 @@
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
 import com.android.launcher3.graphics.IconPalette;
@@ -59,13 +61,6 @@
  */
 public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
 
-    // Dimensions in DP
-    private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
-    private static final float KEY_SHADOW_RADIUS = 1f;
-    private static final float KEY_SHADOW_OFFSET = 0.5f;
-    private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
-    private static final int KEY_SHADOW_COLOR = 0x66000000;
-
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
@@ -75,24 +70,20 @@
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final boolean mCenterVertically;
-    private final Drawable mBackground;
-    private OnLongClickListener mOnLongClickListener;
+
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
     private final StylusEventHelper mStylusEventHelper;
-
-    private boolean mBackgroundSizeChanged;
+    private final float mSlop;
 
     private Bitmap mPressedBackground;
 
-    private float mSlop;
-
     private final boolean mDeferShadowGenerationOnTouch;
-    private final boolean mCustomShadowsEnabled;
     private final boolean mLayoutHorizontal;
     private final int mIconSize;
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mTextColor;
+    private boolean mIsIconVisible = true;
 
     private BadgeInfo mBadgeInfo;
     private BadgeRenderer mBadgeRenderer;
@@ -116,6 +107,19 @@
         }
     };
 
+    public static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
+            = new Property<BubbleTextView, Integer>(Integer.class, "textAlpha") {
+        @Override
+        public Integer get(BubbleTextView bubbleTextView) {
+            return bubbleTextView.getTextAlpha();
+        }
+
+        @Override
+        public void set(BubbleTextView bubbleTextView, Integer alpha) {
+            bubbleTextView.setTextAlpha(alpha);
+        }
+    };
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mStayPressed;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -137,10 +141,10 @@
         super(context, attrs, defStyle);
         mLauncher = Launcher.getLauncher(context);
         DeviceProfile grid = mLauncher.getDeviceProfile();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
-        mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, false);
         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
         mDeferShadowGenerationOnTouch =
                 a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
@@ -149,6 +153,7 @@
         int defaultIconSize = grid.iconSizePx;
         if (display == DISPLAY_WORKSPACE) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+            setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
         } else if (display == DISPLAY_ALL_APPS) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
             setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
@@ -164,23 +169,12 @@
                 defaultIconSize);
         a.recycle();
 
-        if (mCustomShadowsEnabled) {
-            // Draw the background itself as the parent is drawn twice.
-            mBackground = getBackground();
-            setBackground(null);
-
-            // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
-            float density = getResources().getDisplayMetrics().density;
-            setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
-        } else {
-            mBackground = null;
-        }
-
         mLongPressHelper = new CheckLongPressHelper(this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
         setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info) {
@@ -190,7 +184,7 @@
     public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info.iconBitmap, info);
         setTag(info);
-        if (promiseStateChanged || info.isPromise()) {
+        if (promiseStateChanged || (info.hasPromiseIconUi())) {
             applyPromiseState(promiseStateChanged);
         }
 
@@ -206,6 +200,10 @@
         // Verify high res immediately
         verifyHighRes();
 
+        if (info instanceof PromiseAppInfo) {
+            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+            applyProgressLevel(promiseAppInfo.level);
+        }
         applyBadgeState(info, false /* animate */);
     }
 
@@ -238,19 +236,6 @@
     }
 
     @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
-            mBackgroundSizeChanged = true;
-        }
-        return super.setFrame(left, top, right, bottom);
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || super.verifyDrawable(who);
-    }
-
-    @Override
     public void setTag(Object tag) {
         if (tag != null) {
             LauncherModel.checkItemInfo((ItemInfo) tag);
@@ -279,21 +264,6 @@
         return mIcon;
     }
 
-    /** Returns whether the layout is horizontal. */
-    public boolean isLayoutHorizontal() {
-        return mLayoutHorizontal;
-    }
-
-    @Override
-    public void setOnLongClickListener(OnLongClickListener l) {
-        super.setOnLongClickListener(l);
-        mOnLongClickListener = l;
-    }
-
-    public OnLongClickListener getOnLongClickListener() {
-        return mOnLongClickListener;
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // Call the superclass onTouchEvent first, because sometimes it changes the state to
@@ -391,54 +361,14 @@
         return result;
     }
 
+    @SuppressWarnings("wrongcall")
+    protected void drawWithoutBadge(Canvas canvas) {
+        super.onDraw(canvas);
+    }
+
     @Override
-    public void draw(Canvas canvas) {
-        if (!mCustomShadowsEnabled) {
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        final Drawable background = mBackground;
-        if (background != null) {
-            final int scrollX = getScrollX();
-            final int scrollY = getScrollY();
-
-            if (mBackgroundSizeChanged) {
-                background.setBounds(0, 0,  getRight() - getLeft(), getBottom() - getTop());
-                mBackgroundSizeChanged = false;
-            }
-
-            if ((scrollX | scrollY) == 0) {
-                background.draw(canvas);
-            } else {
-                canvas.translate(scrollX, scrollY);
-                background.draw(canvas);
-                canvas.translate(-scrollX, -scrollY);
-            }
-        }
-
-        // If text is transparent, don't draw any shadow
-        if ((getCurrentTextColor() >> 24) == 0) {
-            getPaint().clearShadowLayer();
-            super.draw(canvas);
-            drawBadgeIfNecessary(canvas);
-            return;
-        }
-
-        // We enhance the shadow by drawing the shadow twice
-        float density = getResources().getDisplayMetrics().density;
-        getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
-        super.draw(canvas);
-        canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
-                getScrollX() + getWidth(),
-                getScrollY() + getHeight(), Region.Op.INTERSECT);
-        getPaint().setShadowLayer(
-                density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
-        super.draw(canvas);
-        canvas.restore();
-
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
         drawBadgeIfNecessary(canvas);
     }
 
@@ -446,7 +376,7 @@
      * Draws the icon badge in the top right corner of the icon bounds.
      * @param canvas The canvas to draw to.
      */
-    private void drawBadgeIfNecessary(Canvas canvas) {
+    protected void drawBadgeIfNecessary(Canvas canvas) {
         if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
             getIconBounds(mTempIconBounds);
             mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
@@ -485,14 +415,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mBackground != null) mBackground.setCallback(this);
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mCenterVertically) {
             Paint.FontMetrics fm = getPaint().getFontMetrics();
@@ -506,12 +428,6 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mBackground != null) mBackground.setCallback(null);
-    }
-
-    @Override
     public void setTextColor(int color) {
         mTextColor = color;
         super.setTextColor(color);
@@ -523,15 +439,38 @@
         super.setTextColor(colors);
     }
 
+    public boolean shouldTextBeVisible() {
+        // Text should be visible everywhere but the hotseat.
+        Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
+        ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
+        return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+    }
+
     public void setTextVisibility(boolean visible) {
-        Resources res = getResources();
         if (visible) {
             super.setTextColor(mTextColor);
         } else {
-            super.setTextColor(res.getColor(android.R.color.transparent));
+            setTextAlpha(0);
         }
     }
 
+    private void setTextAlpha(int alpha) {
+        super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
+    }
+
+    private int getTextAlpha() {
+        return Color.alpha(getCurrentTextColor());
+    }
+
+    /**
+     * Creates an animator to fade the text in or out.
+     * @param fadeIn Whether the text should fade in or fade out.
+     */
+    public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
+        int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
+        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
+    }
+
     @Override
     public void cancelLongPress() {
         super.cancelLongPress();
@@ -542,32 +481,41 @@
     public void applyPromiseState(boolean promiseStateChanged) {
         if (getTag() instanceof ShortcutInfo) {
             ShortcutInfo info = (ShortcutInfo) getTag();
-            final boolean isPromise = info.isPromise();
+            final boolean isPromise = info.hasPromiseIconUi();
             final int progressLevel = isPromise ?
                     ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
                             info.getInstallProgress() : 0)) : 100;
 
-            setContentDescription(progressLevel > 0 ?
-                    getContext().getString(R.string.app_downloading_title, info.title,
-                            NumberFormat.getPercentInstance().format(progressLevel * 0.01)) :
-                    getContext().getString(R.string.app_waiting_download_title, info.title));
+            PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+            if (preloadDrawable != null && promiseStateChanged) {
+                preloadDrawable.maybePerformFinishedAnimation();
+            }
+        }
+    }
+
+    public PreloadIconDrawable applyProgressLevel(int progressLevel) {
+        if (getTag() instanceof ItemInfoWithIcon) {
+            ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+            setContentDescription(progressLevel > 0
+                    ? getContext().getString(R.string.app_downloading_title, info.title,
+                    NumberFormat.getPercentInstance().format(progressLevel * 0.01))
+                    : getContext().getString(R.string.app_waiting_download_title, info.title));
 
             if (mIcon != null) {
                 final PreloadIconDrawable preloadDrawable;
                 if (mIcon instanceof PreloadIconDrawable) {
                     preloadDrawable = (PreloadIconDrawable) mIcon;
+                    preloadDrawable.setLevel(progressLevel);
                 } else {
                     preloadDrawable = DrawableFactory.get(getContext())
                             .newPendingIcon(info.iconBitmap, getContext());
+                    preloadDrawable.setLevel(progressLevel);
                     setIcon(preloadDrawable);
                 }
-
-                preloadDrawable.setLevel(progressLevel);
-                if (promiseStateChanged) {
-                    preloadDrawable.maybePerformFinishedAnimation();
-                }
+                return preloadDrawable;
             }
         }
+        return null;
     }
 
     public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
@@ -603,7 +551,21 @@
     private void setIcon(Drawable icon) {
         mIcon = icon;
         mIcon.setBounds(0, 0, mIconSize, mIconSize);
-        applyCompoundDrawables(mIcon);
+        if (mIsIconVisible) {
+            applyCompoundDrawables(mIcon);
+        }
+    }
+
+    public void setIconVisible(boolean visible) {
+        mIsIconVisible = visible;
+        mDisableRelayout = true;
+        Drawable icon = mIcon;
+        if (!visible) {
+            icon = new ColorDrawable(Color.TRANSPARENT);
+            icon.setBounds(0, 0, mIconSize, mIconSize);
+        }
+        applyCompoundDrawables(icon);
+        mDisableRelayout = false;
     }
 
     protected void applyCompoundDrawables(Drawable icon) {
@@ -630,11 +592,16 @@
             mIconLoadRequest = null;
             mDisableRelayout = true;
 
+            // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
+            info.iconBitmap.prepareToDraw();
+
             if (info instanceof AppInfo) {
                 applyFromApplicationInfo((AppInfo) info);
             } else if (info instanceof ShortcutInfo) {
                 applyFromShortcutInfo((ShortcutInfo) info);
-                if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) {
+                FolderIconPreviewVerifier verifier =
+                        new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+                if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
                     View folderIcon =
                             mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
                     if (folderIcon != null) {
@@ -666,6 +633,10 @@
         }
     }
 
+    public int getIconSize() {
+        return mIconSize;
+    }
+
     /**
      * Interface to be implemented by the grand parent to allow click shadow effect.
      */
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8a477d8..632e490 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -29,6 +29,7 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -69,6 +70,7 @@
     /** The paint applied to the drag view on hover */
     protected int mHoverColor = 0;
 
+    protected CharSequence mText;
     protected ColorStateList mOriginalTextColor;
     protected Drawable mDrawable;
 
@@ -96,14 +98,15 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mText = getText();
         mOriginalTextColor = getTextColors();
     }
 
     protected void setDrawable(int resId) {
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
-        mDrawable = getResources().getDrawable(resId);
-        setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+        setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
+        mDrawable = getCompoundDrawablesRelative()[0];
     }
 
     public void setDropTargetBar(DropTargetBar dropTargetBar) {
@@ -297,4 +300,30 @@
     public int getTextColor() {
         return getTextColors().getDefaultColor();
     }
+
+    /**
+     * Returns True if any update was made.
+     */
+    public boolean updateText(boolean hide) {
+        if ((hide && getText().toString().isEmpty()) || (!hide && mText.equals(getText()))) {
+            return false;
+        }
+
+        setText(hide ? "" : mText);
+        return true;
+    }
+
+    public boolean isTextTruncated() {
+        int availableWidth = getMeasuredWidth();
+        if (mHideParentOnDisable) {
+            ViewGroup parent = (ViewGroup) getParent();
+            availableWidth = parent.getMeasuredWidth() - parent.getPaddingLeft()
+                    - parent.getPaddingRight();
+        }
+        availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
+                + getCompoundDrawablePadding());
+        CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
+                TextUtils.TruncateAt.END);
+        return !mText.equals(displayedText);
+    }
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8179dad..a75c6d1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -35,6 +35,7 @@
 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.SparseArray;
@@ -44,28 +45,27 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.DecelerateInterpolator;
-
 import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 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 java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Stack;
 
 public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
@@ -75,7 +75,7 @@
     private static final String TAG = "CellLayout";
     private static final boolean LOGD = false;
 
-    private Launcher mLauncher;
+    private final Launcher mLauncher;
     @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellWidth;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -101,10 +101,10 @@
     private GridOccupancy mTmpOccupied;
 
     private OnTouchListener mInterceptTouchListener;
-    private StylusEventHelper mStylusEventHelper;
+    private final StylusEventHelper mStylusEventHelper;
 
-    private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
-    FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
+    private final ArrayList<PreviewBackground> mFolderBackgrounds = new ArrayList<>();
+    final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
 
     private float mBackgroundAlpha;
 
@@ -121,9 +121,9 @@
 
     // These arrays are used to implement the drag visualization on x-large screens.
     // They are used as circular arrays, indexed by mDragOutlineCurrent.
-    @Thunk Rect[] mDragOutlines = new Rect[4];
-    @Thunk float[] mDragOutlineAlphas = new float[mDragOutlines.length];
-    private InterruptibleInOutAnimator[] mDragOutlineAnims =
+    @Thunk final Rect[] mDragOutlines = new Rect[4];
+    @Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
+    private final InterruptibleInOutAnimator[] mDragOutlineAnims =
             new InterruptibleInOutAnimator[mDragOutlines.length];
 
     // Used as an index into the above 3 arrays; indicates which is the most current value.
@@ -132,8 +132,8 @@
 
     private final ClickShadowView mTouchFeedbackView;
 
-    @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new HashMap<>();
-    @Thunk HashMap<View, ReorderPreviewAnimation> mShakeAnimators = new HashMap<>();
+    @Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
+    @Thunk final ArrayMap<View, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
 
     private boolean mItemPlacementDirty = false;
 
@@ -142,8 +142,8 @@
 
     private boolean mDragging = false;
 
-    private TimeInterpolator mEaseOutInterpolator;
-    private ShortcutAndWidgetContainer mShortcutsAndWidgets;
+    private final TimeInterpolator mEaseOutInterpolator;
+    private final ShortcutAndWidgetContainer mShortcutsAndWidgets;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WORKSPACE, HOTSEAT, FOLDER})
@@ -154,7 +154,7 @@
 
     @ContainerType private final int mContainerType;
 
-    private final float mChildScale;
+    private final float mChildScale = 1f;
 
     public static final int MODE_SHOW_REORDER_HINT = 0;
     public static final int MODE_DRAG_OVER = 1;
@@ -168,10 +168,10 @@
     private static final int REORDER_ANIMATION_DURATION = 150;
     @Thunk final float mReorderPreviewAnimationMagnitude;
 
-    private ArrayList<View> mIntersectingViews = new ArrayList<View>();
-    private Rect mOccupiedRect = new Rect();
-    private int[] mDirectionVector = new int[2];
-    int[] mPreviousReorderDirection = new int[2];
+    private final ArrayList<View> mIntersectingViews = new ArrayList<>();
+    private final Rect mOccupiedRect = new Rect();
+    private final int[] mDirectionVector = new int[2];
+    final int[] mPreviousReorderDirection = new int[2];
     private static final int INVALID_DIRECTION = -100;
 
     private final Rect mTempRect = new Rect();
@@ -218,8 +218,6 @@
         mFolderLeaveBehind.delegateCellX = -1;
         mFolderLeaveBehind.delegateCellY = -1;
 
-        mChildScale = mContainerType == HOTSEAT ? grid.inv.hotseatScale : 1f;
-
         setAlwaysDrawnWithCacheEnabled(false);
         final Resources res = getResources();
 
@@ -235,7 +233,7 @@
         for (int i = 0; i < mDragOutlines.length; i++) {
             mDragOutlines[i] = new Rect(-1, -1, -1, -1);
         }
-        mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
+        mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
 
         // When dragging things around the home screens, we show a green outline of
         // where the item will land. The outlines gradually fade out, leaving a trail
@@ -496,7 +494,7 @@
         }
 
         for (int i = 0; i < mFolderBackgrounds.size(); i++) {
-            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            PreviewBackground bg = mFolderBackgrounds.get(i);
             cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
@@ -522,7 +520,7 @@
         super.dispatchDraw(canvas);
 
         for (int i = 0; i < mFolderBackgrounds.size(); i++) {
-            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            PreviewBackground bg = mFolderBackgrounds.get(i);
             if (bg.isClipping) {
                 cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
                 canvas.save();
@@ -533,19 +531,16 @@
         }
     }
 
-    public void addFolderBackground(FolderIcon.PreviewBackground bg) {
+    public void addFolderBackground(PreviewBackground bg) {
         mFolderBackgrounds.add(bg);
     }
-    public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
+    public void removeFolderBackground(PreviewBackground bg) {
         mFolderBackgrounds.remove(bg);
     }
 
     public void setFolderLeaveBehindCell(int x, int y) {
-
-        DeviceProfile grid = mLauncher.getDeviceProfile();
         View child = getChildAt(x, y);
-
-        mFolderLeaveBehind.setup(getResources().getDisplayMetrics(), grid, null,
+        mFolderLeaveBehind.setup(mLauncher, null,
                 child.getMeasuredWidth(), child.getPaddingTop());
 
         mFolderLeaveBehind.delegateCellX = x;
@@ -568,7 +563,7 @@
         try {
             dispatchRestoreInstanceState(states);
         } catch (IllegalArgumentException ex) {
-            if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_DOGFOOD_BUILD) {
                 throw ex;
             }
             // Mismatched viewId / viewType preventing restore. Skip restore on production builds.
@@ -785,7 +780,7 @@
         return mCellWidth;
     }
 
-    int getCellHeight() {
+    public int getCellHeight() {
         return mCellHeight;
     }
 
@@ -865,10 +860,10 @@
         // Expand the background drawing bounds by the padding baked into the background drawable
         mBackground.getPadding(mTempRect);
         mBackground.setBounds(
-                left - mTempRect.left,
-                top - mTempRect.top,
-                right + mTempRect.right,
-                bottom + mTempRect.bottom);
+                left - mTempRect.left - getPaddingLeft(),
+                top - mTempRect.top - getPaddingTop(),
+                right + mTempRect.right + getPaddingRight(),
+                bottom + mTempRect.bottom + getPaddingBottom());
     }
 
     /**
@@ -1102,7 +1097,7 @@
                 result, resultSpan);
     }
 
-    private final Stack<Rect> mTempRectStack = new Stack<Rect>();
+    private final Stack<Rect> mTempRectStack = new Stack<>();
     private void lazyInitTempRectStack() {
         if (mTempRectStack.isEmpty()) {
             for (int i = 0; i < mCountX * mCountY; i++) {
@@ -1147,7 +1142,7 @@
         final int[] bestXY = result != null ? result : new int[2];
         double bestDistance = Double.MAX_VALUE;
         final Rect bestRect = new Rect(-1, -1, -1, -1);
-        final Stack<Rect> validRegions = new Stack<Rect>();
+        final Stack<Rect> validRegions = new Stack<>();
 
         final int countX = mCountX;
         final int countY = mCountY;
@@ -1349,14 +1344,14 @@
         final static int RIGHT = 1 << 2;
         final static int BOTTOM = 1 << 3;
 
-        ArrayList<View> views;
-        ItemConfiguration config;
-        Rect boundingRect = new Rect();
+        final ArrayList<View> views;
+        final ItemConfiguration config;
+        final Rect boundingRect = new Rect();
 
-        int[] leftEdge = new int[mCountY];
-        int[] rightEdge = new int[mCountY];
-        int[] topEdge = new int[mCountX];
-        int[] bottomEdge = new int[mCountX];
+        final int[] leftEdge = new int[mCountY];
+        final int[] rightEdge = new int[mCountY];
+        final int[] topEdge = new int[mCountX];
+        final int[] bottomEdge = new int[mCountX];
         int dirtyEdges;
         boolean boundingRectDirty;
 
@@ -1496,7 +1491,7 @@
             return boundingRect;
         }
 
-        PositionComparator comparator = new PositionComparator();
+        final PositionComparator comparator = new PositionComparator();
         class PositionComparator implements Comparator<View> {
             int whichEdge = 0;
             public int compare(View left, View right) {
@@ -1796,7 +1791,7 @@
             }
         }
 
-        solution.intersectingViews = new ArrayList<View>(mIntersectingViews);
+        solution.intersectingViews = new ArrayList<>(mIntersectingViews);
 
         // First we try to find a solution which respects the push mechanic. That is,
         // we try to find a solution such that no displaced item travels through another item
@@ -1852,7 +1847,7 @@
         int result[] = new int[2];
         result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
 
-        boolean success = false;
+        boolean success;
         // First we try the exact nearest position of the item being dragged,
         // we will then want to try to move this around to other neighbouring positions
         success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
@@ -1960,14 +1955,14 @@
     // 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 {
-        View child;
+        final View child;
         float finalDeltaX;
         float finalDeltaY;
         float initDeltaX;
         float initDeltaY;
-        float finalScale;
+        final float finalScale;
         float initScale;
-        int mode;
+        final int mode;
         boolean repeating = false;
         private static final int PREVIEW_DURATION = 300;
         private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
@@ -2417,9 +2412,9 @@
     }
 
     private static class ItemConfiguration extends CellAndSpan {
-        HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
-        private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
-        ArrayList<View> sortedViews = new ArrayList<View>();
+        final ArrayMap<View, CellAndSpan> map = new ArrayMap<>();
+        private final ArrayMap<View, CellAndSpan> savedMap = new ArrayMap<>();
+        final ArrayList<View> sortedViews = new ArrayList<>();
         ArrayList<View> intersectingViews;
         boolean isSolution = false;
 
@@ -2469,7 +2464,6 @@
      * @param pixelY The Y location at which you want to search for a vacant area.
      * @param spanX Horizontal span of the object.
      * @param spanY Vertical span of the object.
-     * @param ignoreView Considers space occupied by this view as unoccupied
      * @param result Previously returned value to possibly recycle.
      * @return The X, Y cell of a vacant area that can contain this object,
      *         nearest the requested location.
@@ -2781,9 +2775,9 @@
     //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
     //    the CellLayout that was long clicked
     public static final class CellInfo extends CellAndSpan {
-        public View cell;
-        long screenId;
-        long container;
+        public final View cell;
+        final long screenId;
+        final long container;
 
         public CellInfo(View v, ItemInfo info) {
             cellX = info.cellX;
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 05911ab..1ec30ba 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -13,18 +13,15 @@
 import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.net.URISyntaxException;
-import java.util.HashMap;
 import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 /**
  * Implements the layout parser with rules for internal layouts and partner layouts.
@@ -55,20 +52,20 @@
     }
 
     @Override
-    protected HashMap<String, TagParser> getFolderElementsMap() {
+    protected ArrayMap<String, TagParser> getFolderElementsMap() {
         return getFolderElementsMap(mSourceRes);
     }
 
-    @Thunk HashMap<String, TagParser> getFolderElementsMap(Resources res) {
-        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+    @Thunk ArrayMap<String, TagParser> getFolderElementsMap(Resources res) {
+        ArrayMap<String, TagParser> parsers = new ArrayMap<>();
         parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
         parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
         return parsers;
     }
 
     @Override
-    protected HashMap<String, TagParser> getLayoutElementsMap() {
-        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+    protected ArrayMap<String, TagParser> getLayoutElementsMap() {
+        ArrayMap<String, TagParser> parsers = new ArrayMap<>();
         parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
         parsers.put(TAG_APPWIDGET, new AppWidgetParser());
         parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java
deleted file mode 100644
index a43ab67..0000000
--- a/src/com/android/launcher3/DeferredHandler.java
+++ /dev/null
@@ -1,120 +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.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.LinkedList;
-
-/**
- * Queue of things to run on a looper thread.  Items posted with {@link #post} will not
- * be actually enqued on the handler until after the last one has run, to keep from
- * starving the thread.
- *
- * This class is fifo.
- */
-public class DeferredHandler {
-    @Thunk LinkedList<Runnable> mQueue = new LinkedList<>();
-    private MessageQueue mMessageQueue = Looper.myQueue();
-    private Impl mHandler = new Impl();
-
-    @Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
-        public void handleMessage(Message msg) {
-            Runnable r;
-            synchronized (mQueue) {
-                if (mQueue.size() == 0) {
-                    return;
-                }
-                r = mQueue.removeFirst();
-            }
-            r.run();
-            synchronized (mQueue) {
-                scheduleNextLocked();
-            }
-        }
-
-        public boolean queueIdle() {
-            handleMessage(null);
-            return false;
-        }
-    }
-
-    private class IdleRunnable implements Runnable {
-        Runnable mRunnable;
-
-        IdleRunnable(Runnable r) {
-            mRunnable = r;
-        }
-
-        public void run() {
-            mRunnable.run();
-        }
-    }
-
-    public DeferredHandler() {
-    }
-
-    /** Schedule runnable to run after everything that's on the queue right now. */
-    public void post(Runnable runnable) {
-        synchronized (mQueue) {
-            mQueue.add(runnable);
-            if (mQueue.size() == 1) {
-                scheduleNextLocked();
-            }
-        }
-    }
-
-    /** Schedule runnable to run when the queue goes idle. */
-    public void postIdle(final Runnable runnable) {
-        post(new IdleRunnable(runnable));
-    }
-
-    public void cancelAll() {
-        synchronized (mQueue) {
-            mQueue.clear();
-        }
-    }
-
-    /** Runs all queued Runnables from the calling thread. */
-    public void flush() {
-        LinkedList<Runnable> queue = new LinkedList<>();
-        synchronized (mQueue) {
-            queue.addAll(mQueue);
-            mQueue.clear();
-        }
-        for (Runnable r : queue) {
-            r.run();
-        }
-    }
-
-    void scheduleNextLocked() {
-        if (mQueue.size() > 0) {
-            Runnable peek = mQueue.getFirst();
-            if (peek instanceof IdleRunnable) {
-                mMessageQueue.addIdleHandler(mHandler);
-            } else {
-                mHandler.sendEmptyMessage(1);
-            }
-        }
-    }
-}
-
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9097ed2..4dcb64f 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -40,7 +40,7 @@
         // Get the hover color
         mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
 
-        setDrawable(R.drawable.ic_remove_launcher);
+        setDrawable(R.drawable.ic_remove_shadow);
     }
 
     @Override
@@ -65,9 +65,11 @@
      * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
      */
     public void setTextBasedOnDragSource(DragSource dragSource) {
-        if (!TextUtils.isEmpty(getText())) {
-            setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
+        if (!TextUtils.isEmpty(mText)) {
+            mText = getResources().getString(dragSource.supportsDeleteDropTarget()
+                    ? R.string.remove_drop_target_label
                     : android.R.string.cancel);
+            requestLayout();
         }
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e47031a..69ee03e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -19,6 +19,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -32,7 +33,6 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
 
@@ -63,6 +63,8 @@
      */
     private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
 
+    private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f;
+
     // Overview mode
     private final int overviewModeMinIconZoneHeightPx;
     private final int overviewModeMaxIconZoneHeightPx;
@@ -71,7 +73,9 @@
     private final float overviewModeIconZoneRatio;
 
     // Workspace
-    private int desiredWorkspaceLeftRightMarginPx;
+    private final int desiredWorkspaceLeftRightMarginPx;
+    public final int cellLayoutPaddingLeftRightPx;
+    public final int cellLayoutBottomPaddingPx;
     public final int edgeMarginPx;
     public final Rect defaultWidgetPadding;
     private final int defaultPageSpacingPx;
@@ -80,9 +84,9 @@
     public final int workspaceSpringLoadedBottomSpace;
 
     // Page indicator
-    private final int pageIndicatorHeightPx;
-    private final int pageIndicatorLandGutterLeftNavBarPx;
-    private final int pageIndicatorLandGutterRightNavBarPx;
+    private int pageIndicatorSizePx;
+    private final int pageIndicatorLandLeftNavBarGutterPx;
+    private final int pageIndicatorLandRightNavBarGutterPx;
     private final int pageIndicatorLandWorkspaceOffsetPx;
 
     // Workspace icons
@@ -93,6 +97,7 @@
 
     public int cellWidthPx;
     public int cellHeightPx;
+    public int workspaceCellPaddingXPx;
 
     // Folder
     public int folderBackgroundOffset;
@@ -109,15 +114,20 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
-    public int hotseatCellWidthPx;
     public int hotseatCellHeightPx;
-    public int hotseatIconSizePx;
-    public int hotseatBarHeightPx;
-    private int hotseatBarTopPaddingPx;
-    private int hotseatBarBottomPaddingPx;
-    private int hotseatLandGutterPx;
+    // In portrait: size = height, in landscape: size = width
+    public int hotseatBarSizePx;
+    public int hotseatBarTopPaddingPx;
+    public int hotseatBarBottomPaddingPx;
+
+    public int hotseatBarLeftNavBarLeftPaddingPx;
+    public int hotseatBarLeftNavBarRightPaddingPx;
+
+    public int hotseatBarRightNavBarLeftPaddingPx;
+    public int hotseatBarRightNavBarRightPaddingPx;
 
     // All apps
+    public int allAppsCellHeightPx;
     public int allAppsNumCols;
     public int allAppsNumPredictiveCols;
     public int allAppsButtonVisualSize;
@@ -159,19 +169,29 @@
         transposeLayoutWithOrientation =
                 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
 
+        context = getContext(context, isVerticalBarLayout()
+                ? Configuration.ORIENTATION_LANDSCAPE
+                : 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 = edgeMarginPx;
-        pageIndicatorHeightPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
-        pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
+        desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+        cellLayoutPaddingLeftRightPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+        cellLayoutBottomPaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
+        pageIndicatorSizePx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_min_page_indicator_size);
+        pageIndicatorLandLeftNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_land_left_nav_bar_gutter_width);
+        pageIndicatorLandRightNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_land_right_nav_bar_gutter_width);
         pageIndicatorLandWorkspaceOffsetPx =
                 res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
-        pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
         topWorkspacePadding =
@@ -191,11 +211,25 @@
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
-        hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
+
+        workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
+
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
-        hotseatBarBottomPaddingPx = 0;
-        hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
+        hotseatBarBottomPaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+        hotseatBarLeftNavBarRightPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_right_padding);
+        hotseatBarRightNavBarRightPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_right_padding);
+        hotseatBarLeftNavBarLeftPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_left_padding);
+        hotseatBarRightNavBarLeftPaddingPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_left_padding);
+        hotseatBarSizePx = isVerticalBarLayout()
+                ? Utilities.pxFromDp(inv.iconSize, dm)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
+                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
 
         // Determine sizes.
         widthPx = width;
@@ -208,8 +242,24 @@
             availableHeightPx = maxSize.y;
         }
 
-        // Calculate the remaining vars
+        // Calculate all of the remaining variables.
         updateAvailableDimensions(dm, res);
+
+        // Now that we have all of the variables calculated, we can tune certain sizes.
+        float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
+        boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
+        if (!isVerticalBarLayout() && isPhone && isTallDevice) {
+            // We increase the hotseat size when there is extra space.
+            // ie. For a display with a large aspect ratio, we can keep the icons on the workspace
+            // in portrait mode closer together by adding more height to the hotseat.
+            // Note: This calculation was created after noticing a pattern in the design spec.
+            int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx;
+            hotseatBarSizePx += extraSpace - pageIndicatorSizePx;
+
+            // Recalculate the available dimensions using the new hotseat size.
+            updateAvailableDimensions(dm, res);
+        }
+
         computeAllAppsButtonSize(context);
 
         // This is done last, after iconSizePx is calculated above.
@@ -217,6 +267,10 @@
     }
 
     DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+        // We take the minimum sizes of this profile and it's multi-window variant to ensure that
+        // the system decor is always excluded.
+        mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
+
         // In multi-window mode, we can have widthPx = availableWidthPx
         // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
         // widthPx and heightPx values where it's needed.
@@ -224,12 +278,7 @@
                 isLandscape);
 
         // Hide labels on the workspace.
-        profile.iconTextSizePx = 0;
-        profile.cellHeightPx = profile.iconSizePx + profile.iconDrawablePaddingPx
-                + Utilities.calculateTextHeight(profile.iconTextSizePx);
-
-        // The nav bar is black so we add bottom padding to visually center hotseat icons.
-        profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
+        profile.adjustToHideWorkspaceLabels();
 
         // We use these scales to measure and layout the widgets using their full invariant profile
         // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
@@ -253,51 +302,87 @@
     }
 
     /**
+     * Adjusts the profile so that the labels on the Workspace are hidden.
+     * It is important to call this method after the All Apps variables have been set.
+     */
+    private void adjustToHideWorkspaceLabels() {
+        iconTextSizePx = 0;
+        iconDrawablePaddingPx = 0;
+        cellHeightPx = iconSizePx;
+
+        // In normal cases, All Apps cell height should equal the Workspace cell height.
+        // Since we are removing labels from the Workspace, we need to manually compute the
+        // All Apps cell height.
+        int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1);
+        allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
+                + Utilities.calculateTextHeight(allAppsIconTextSizePx)
+                + topBottomPadding * 2;
+    }
+
+    /**
      * Determine the exact visual footprint of the all apps button, taking into account scaling
      * and internal padding of the drawable.
      */
     private void computeAllAppsButtonSize(Context context) {
         Resources res = context.getResources();
         float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
-        allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
+        allAppsButtonVisualSize = (int) (iconSizePx * (1 - padding)) - context.getResources()
                         .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
     }
 
     private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
-        updateIconSize(1f, iconDrawablePaddingOriginalPx, res, dm);
+        updateIconSize(1f, res, dm);
 
         // Check to see if the icons fit within the available height.  If not, then scale down.
         float usedHeight = (cellHeightPx * inv.numRows);
         int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
         if (usedHeight > maxHeight) {
             float scale = maxHeight / usedHeight;
-            updateIconSize(scale, 0, res, dm);
+            updateIconSize(scale, res, dm);
         }
-
         updateAvailableFolderCellDimensions(dm, res);
     }
 
-    private void updateIconSize(float scale, int drawablePadding, Resources res,
-                                DisplayMetrics dm) {
-        iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+    private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
+        // Workspace
+        float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+        iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
-        iconDrawablePaddingPx = drawablePadding;
-        hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
-        allAppsIconSizePx = iconSizePx;
-        allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
-        allAppsIconTextSizePx = iconTextSizePx;
+        iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
-        cellWidthPx = iconSizePx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
+        int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
+        if (iconDrawablePaddingPx > cellYPadding && !isVerticalBarLayout()
+                && !inMultiWindowMode()) {
+            // Ensures that the label is closer to its corresponding icon. This is not an issue
+            // with vertical bar layout or multi-window mode since the issue is handled separately
+            // with their calls to {@link #adjustToHideWorkspaceLabels}.
+            cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
+            iconDrawablePaddingPx = cellYPadding;
+        }
+        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
+
+        // All apps
+        allAppsIconTextSizePx = iconTextSizePx;
+        allAppsIconSizePx = iconSizePx;
+        allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
+        allAppsCellHeightPx = getCellSize().y;
+
+        if (isVerticalBarLayout()) {
+            // Always hide the Workspace text with vertical bar layout.
+            adjustToHideWorkspaceLabels();
+        }
 
         // Hotseat
-        hotseatCellWidthPx = iconSizePx;
+        if (isVerticalBarLayout()) {
+            hotseatBarSizePx = iconSizePx;
+        }
         hotseatCellHeightPx = iconSizePx;
 
         if (!isVerticalBarLayout()) {
-            int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
-                    - pageIndicatorHeightPx - topWorkspacePadding;
+            int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
+                    - pageIndicatorSizePx - topWorkspacePadding;
             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
             workspaceSpringLoadShrinkFactor = Math.min(
                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -308,7 +393,7 @@
         }
 
         // Folder icon
-        folderBackgroundOffset = -edgeMarginPx;
+        folderBackgroundOffset = -iconDrawablePaddingPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
         folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
     }
@@ -321,7 +406,7 @@
         updateFolderCellSize(1f, dm, res);
 
         // Don't let the folder get too close to the edges of the screen.
-        int folderMargin = 4 * edgeMarginPx;
+        int folderMargin = edgeMarginPx;
 
         // Check if the icons fit within the available height.
         float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
@@ -389,8 +474,10 @@
         // Since we are only concerned with the overall padding, layout direction does
         // not matter.
         Point padding = getTotalWorkspacePadding();
-        result.x = calculateCellWidth(availableWidthPx - padding.x, inv.numColumns);
-        result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows);
+        result.x = calculateCellWidth(availableWidthPx - padding.x
+                - cellLayoutPaddingLeftRightPx * 2, inv.numColumns);
+        result.y = calculateCellHeight(availableHeightPx - padding.y
+                - cellLayoutBottomPaddingPx, inv.numRows);
         return result;
     }
 
@@ -401,22 +488,26 @@
 
     /**
      * Returns the workspace padding in the specified orientation.
-     * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
-     * this value is not reliable.
-     * Use {@link #getTotalWorkspacePadding()} instead.
      */
     public Rect getWorkspacePadding(Rect recycle) {
         Rect padding = recycle == null ? new Rect() : recycle;
         if (isVerticalBarLayout()) {
             if (mInsets.left > 0) {
-                padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
-                        hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
+                padding.set(mInsets.left + pageIndicatorLandLeftNavBarGutterPx,
+                        0,
+                        hotseatBarSizePx + hotseatBarLeftNavBarRightPaddingPx
+                                + hotseatBarLeftNavBarLeftPaddingPx
+                                - mInsets.left,
+                        edgeMarginPx);
             } else {
-                padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
-                        hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
+                padding.set(pageIndicatorLandRightNavBarGutterPx,
+                        0,
+                        hotseatBarSizePx + hotseatBarRightNavBarRightPaddingPx
+                                + hotseatBarRightNavBarLeftPaddingPx,
+                        edgeMarginPx);
             }
         } else {
-            int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
+            int paddingBottom = hotseatBarSizePx + pageIndicatorSizePx;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
@@ -451,15 +542,15 @@
             // Folders should only appear right of the drop target bar and left of the hotseat
             return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
                     mInsets.top,
-                    mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx,
+                    mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx,
                     mInsets.top + availableHeightPx);
         } else {
             // Folders should only appear below the drop target bar and above the hotseat
             return new Rect(mInsets.left,
                     mInsets.top + dropTargetBarSizePx + edgeMarginPx,
                     mInsets.left + availableWidthPx,
-                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
-                            edgeMarginPx);
+                    mInsets.top + availableHeightPx - hotseatBarSizePx
+                            - pageIndicatorSizePx - edgeMarginPx);
         }
     }
 
@@ -477,9 +568,9 @@
 
     int getOverviewModeButtonBarHeight() {
         int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
-        zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
-                Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
-        return zoneHeight;
+        return Utilities.boundToRange(zoneHeight,
+                overviewModeMinIconZoneHeightPx,
+                overviewModeMaxIconZoneHeightPx);
     }
 
     public static int calculateCellWidth(int width, int countX) {
@@ -532,14 +623,6 @@
                 workspacePadding.bottom);
         workspace.setPageSpacing(getWorkspacePageSpacing());
 
-        // Only display when enabled
-        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
-            View qsbContainer = launcher.getQsbContainer();
-            lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
-            lp.topMargin = mInsets.top + workspacePadding.top;
-            qsbContainer.setLayoutParams(lp);
-        }
-
         // Layout the hotseat
         Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
@@ -553,28 +636,44 @@
         if (hasVerticalBarLayout) {
             // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
             //                     screen regardless of RTL
+            int paddingRight = mInsets.left > 0
+                    ? hotseatBarLeftNavBarRightPaddingPx
+                    : hotseatBarRightNavBarRightPaddingPx;
+            int paddingLeft = mInsets.left > 0
+                    ? hotseatBarLeftNavBarLeftPaddingPx
+                    : hotseatBarRightNavBarLeftPaddingPx;
+
             lp.gravity = Gravity.RIGHT;
-            lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
+            lp.width = hotseatBarSizePx + mInsets.left + mInsets.right
+                    + paddingLeft + paddingRight;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
-                    workspacePadding.bottom);
+
+            hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx
+                            + paddingLeft,
+                    mInsets.top,
+                    mInsets.right + cellLayoutPaddingLeftRightPx + paddingRight,
+                    workspacePadding.bottom + cellLayoutBottomPaddingPx);
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
-            lp.height = hotseatBarHeightPx + mInsets.bottom;
-            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
-                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
-                    hotseatBarBottomPaddingPx + mInsets.bottom);
+            lp.height = hotseatBarSizePx + mInsets.bottom;
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
+                            + cellLayoutPaddingLeftRightPx,
+                    hotseatBarTopPaddingPx,
+                    hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+                    hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
         } else {
             // For phones, layout the hotseat without any bottom margin
             // to ensure that we have space for the folders
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
-            lp.height = hotseatBarHeightPx + mInsets.bottom;
-            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
-                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
-                    hotseatBarBottomPaddingPx + mInsets.bottom);
+            lp.height = hotseatBarSizePx + mInsets.bottom;
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
+                            + cellLayoutPaddingLeftRightPx,
+                    hotseatBarTopPaddingPx,
+                    hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+                    hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
         }
         hotseat.setLayoutParams(lp);
 
@@ -584,18 +683,16 @@
             lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
             if (isVerticalBarLayout()) {
                 if (mInsets.left > 0) {
-                    lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
-                            lp.width - pageIndicatorLandWorkspaceOffsetPx;
-                } else if (mInsets.right > 0) {
-                    lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
-                            pageIndicatorLandWorkspaceOffsetPx;
+                    lp.leftMargin = mInsets.left;
+                } else {
+                    lp.leftMargin = pageIndicatorLandWorkspaceOffsetPx;
                 }
                 lp.bottomMargin = workspacePadding.bottom;
             } else {
                 // Put the page indicators above the hotseat
                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                lp.height = pageIndicatorHeightPx;
-                lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
+                lp.height = pageIndicatorSizePx;
+                lp.bottomMargin = hotseatBarSizePx + mInsets.bottom;
             }
             pageIndicator.setLayoutParams(lp);
         }
@@ -609,10 +706,17 @@
 
             lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
             lp.width = Math.min(availableWidthPx, maxWidth);
-            lp.height = getOverviewModeButtonBarHeight() + mInsets.bottom;
+            lp.height = getOverviewModeButtonBarHeight();
+            lp.bottomMargin = mInsets.bottom;
             overviewMode.setLayoutParams(lp);
         }
 
+        // Layout the AllAppsRecyclerView
+        View view = launcher.findViewById(R.id.apps_list_view);
+        int paddingLeftRight = desiredWorkspaceLeftRightMarginPx + cellLayoutPaddingLeftRightPx;
+        view.setPadding(paddingLeftRight, view.getPaddingTop(), paddingLeftRight,
+                view.getPaddingBottom());
+
         if (notifyListeners) {
             for (int i = mListeners.size() - 1; i >= 0; i--) {
                 mListeners.get(i).onLauncherLayoutChanged();
@@ -656,15 +760,24 @@
         }
 
         // In landscape, we match the width of the workspace
-        int padding = (pageIndicatorLandGutterRightNavBarPx +
-                hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
-        return new int[]{ padding, padding };
+        Rect padding = getWorkspacePadding(null);
+        return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
+    }
+
+    public boolean inMultiWindowMode() {
+        return this != inv.landscapeProfile && this != inv.portraitProfile;
     }
 
     public boolean shouldIgnoreLongPressToOverview(float touchX) {
-        boolean inMultiWindowMode = this != inv.landscapeProfile && this != inv.portraitProfile;
         boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
         boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
-        return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
+        return !inMultiWindowMode() && (touchedLhsEdge || touchedRhsEdge);
+    }
+
+    private static Context getContext(Context c, int orientation) {
+        Configuration context = new Configuration(c.getResources().getConfiguration());
+        context.orientation = orientation;
+        return c.createConfigurationContext(context);
+
     }
 }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 0840b70..29a1349 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -78,6 +78,58 @@
         setupButtonDropTarget(this, dragController);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
+        if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
+            // Text has changed, so we need to re-measure.
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    /**
+     * Helper method that iterates through the children and returns whether any of the visible
+     * {@link ButtonDropTarget} has truncated text.
+     *
+     * @param shouldUpdateText If True, updates the text of all children.
+     * @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
+     *                 children; otherwise it sets the original text value.
+     *
+     *
+     * @return If shouldUpdateText is True, returns whether any of the children updated their text.
+     *         Else, returns whether any of the children have truncated their text.
+     */
+    private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
+        boolean result = false;
+        View visibleView;
+        ButtonDropTarget dropTarget;
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            if (getChildAt(i) instanceof ButtonDropTarget) {
+                visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
+            } else if (getChildAt(i) instanceof ViewGroup) {
+                // The Drop Target is wrapped in a FrameLayout.
+                visibleView = getChildAt(i);
+                dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
+            } else {
+                // Ignore other views.
+                continue;
+            }
+
+            if (visibleView.getVisibility() == View.VISIBLE) {
+                if (shouldUpdateText) {
+                    result |= dropTarget.updateText(hideText);
+                } else if (dropTarget.isTextTruncated()) {
+                    result = true;
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
     private void setupButtonDropTarget(View view, DragController dragController) {
         if (view instanceof ButtonDropTarget) {
             ButtonDropTarget bdt = (ButtonDropTarget) view;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 199baaf..1272e0a 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -36,8 +36,6 @@
 
 public class FastBitmapDrawable extends Drawable {
 
-    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
-
     private static final float PRESSED_BRIGHTNESS = 100f / 255f;
     private static final float DISABLED_DESATURATION = 1f;
     private static final float DISABLED_BRIGHTNESS = 0.5f;
@@ -107,17 +105,6 @@
 
     @Override
     public void draw(Canvas canvas) {
-        drawInternal(canvas);
-    }
-
-    public void drawWithBrightness(Canvas canvas, float brightness) {
-        float oldBrightness = getBrightness();
-        setBrightness(brightness);
-        drawInternal(canvas);
-        setBrightness(oldBrightness);
-    }
-
-    protected void drawInternal(Canvas canvas) {
         canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
     }
 
@@ -185,6 +172,11 @@
     }
 
     @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
     protected boolean onStateChange(int[] state) {
         boolean isPressed = false;
         for (int s : state) {
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a51ddd4..3cbc989 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -23,7 +23,6 @@
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
-
 import com.android.launcher3.util.Thunk;
 
 /*
@@ -33,10 +32,11 @@
  */
 public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
     implements ValueAnimator.AnimatorUpdateListener {
+    private static final String TAG = "FirstFrameAnimatorHlpr";
     private static final boolean DEBUG = false;
     private static final int MAX_DELAY = 1000;
     private static final int IDEAL_FRAME_DURATION = 16;
-    private View mTarget;
+    private final View mTarget;
     private long mStartFrame;
     private long mStartTime = -1;
     private boolean mHandlingOnAnimationUpdate;
@@ -77,7 +77,7 @@
                     sGlobalFrameCounter++;
                     if (DEBUG) {
                         long newTime = System.currentTimeMillis();
-                        Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+                        Log.d(TAG, "TICK " + (newTime - mTime));
                         mTime = newTime;
                     }
                 }
@@ -139,7 +139,7 @@
 
     public void print(ValueAnimator animation) {
         float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
-        Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+        Log.d(TAG, sGlobalFrameCounter +
               "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
               mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
     }
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b36734b..fe7acda 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,7 +22,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderPagedView;
 import com.android.launcher3.util.FocusLogic;
@@ -93,7 +93,7 @@
             }
 
             if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
-                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_DOGFOOD_BUILD) {
                     throw new IllegalStateException("Parent of the focused item is not supported.");
                 } else {
                     return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 0041bb4..21254ab 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -65,9 +65,17 @@
      * @param item
      */
     public void add(ShortcutInfo item, boolean animate) {
-        contents.add(item);
+        add(item, contents.size(), animate);
+    }
+
+    /**
+     * Add an app or shortcut for a specified rank.
+     */
+    public void add(ShortcutInfo item, int rank, boolean animate) {
+        rank = Utilities.boundToRange(rank, 0, contents.size());
+        contents.add(rank, item);
         for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onAdd(item);
+            listeners.get(i).onAdd(item, rank);
         }
         itemsChanged(animate);
     }
@@ -121,7 +129,7 @@
     }
 
     public interface FolderListener {
-        public void onAdd(ShortcutInfo item);
+        public void onAdd(ShortcutInfo item, int rank);
         public void onRemove(ShortcutInfo item);
         public void onTitleChanged(CharSequence title);
         public void onItemsChanged(boolean animate);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 47052a7..a6d80e3 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -72,7 +72,9 @@
         mBackgroundColor = ColorUtils.setAlphaComponent(
                 Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
         mBackground = new ColorDrawable(mBackgroundColor);
-        setBackground(mBackground);
+        if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            setBackground(mBackground);
+        }
     }
 
     public CellLayout getLayout() {
@@ -147,9 +149,7 @@
             allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
             if (mLauncher != null) {
                 mLauncher.setAllAppsButton(allAppsButton);
-                allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
                 allAppsButton.setOnClickListener(mLauncher);
-                allAppsButton.setOnLongClickListener(mLauncher);
                 allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
             }
 
@@ -179,8 +179,12 @@
     }
 
     public void updateColor(ExtractedColors extractedColors, boolean animate) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            // not hotseat visible
+            return;
+        }
         if (!mHasVerticalHotseat) {
-            int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+            int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
             if (mBackgroundColorAnimator != null) {
                 mBackgroundColorAnimator.cancel();
             }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 1217030..573e8a2 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -32,10 +32,6 @@
 import android.database.sqlite.SQLiteException;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
@@ -45,19 +41,17 @@
 import android.support.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
-
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 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.Themes;
 import com.android.launcher3.util.Thunk;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -75,7 +69,7 @@
     private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
 
     // Empty class name is used for storing package default entry.
-    private static final String EMPTY_CLASS_NAME = ".";
+    public static final String EMPTY_CLASS_NAME = ".";
 
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_IGNORE_CACHE = false;
@@ -96,45 +90,32 @@
 
     private final Context mContext;
     private final PackageManager mPackageManager;
-    private IconProvider mIconProvider;
+    private final IconProvider mIconProvider;
     @Thunk final UserManagerCompat mUserManager;
     private final LauncherAppsCompat mLauncherApps;
     private final HashMap<ComponentKey, CacheEntry> mCache =
-            new HashMap<ComponentKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
+            new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
+    private final InstantAppResolver mInstantAppResolver;
     private final int mIconDpi;
     @Thunk final IconDB mIconDb;
 
     @Thunk final Handler mWorkerHandler;
 
-    // The background color used for activity icons. Since these icons are displayed in all-apps
-    // and folders, this would be same as the light quantum panel background. This color
-    // is used to convert icons to RGB_565.
-    private final int mActivityBgColor;
-    // The background color used for package icons. These are displayed in widget tray, which
-    // has a dark quantum panel background.
-    private final int mPackageBgColor;
     private final BitmapFactory.Options mLowResOptions;
 
-    private Canvas mLowResCanvas;
-    private Paint mLowResPaint;
-
     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);
-        mLowResCanvas = new Canvas();
-        mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
 
         mIconProvider = Utilities.getOverrideObject(
                 IconProvider.class, context, R.string.icon_provider_class);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
 
-        mActivityBgColor = Themes.getColorPrimary(context, R.style.LauncherTheme);
-        mPackageBgColor = Themes.getColorPrimary(context, R.style.WidgetContainerTheme);
-
         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.
@@ -142,7 +123,8 @@
     }
 
     private Drawable getFullResDefaultActivityIcon() {
-        return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon);
+        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) {
@@ -190,7 +172,11 @@
     }
 
     public Drawable getFullResIcon(LauncherActivityInfo info) {
-        return mIconProvider.getIcon(info, mIconDpi);
+        return getFullResIcon(info, true);
+    }
+
+    public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+        return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
     }
 
     protected Bitmap makeDefaultIcon(UserHandle user) {
@@ -209,7 +195,7 @@
      * Remove any records for the supplied package name from memory.
      */
     private void removeFromMemCacheLocked(String packageName, UserHandle user) {
-        HashSet<ComponentKey> forDeletion = new HashSet<ComponentKey>();
+        HashSet<ComponentKey> forDeletion = new HashSet<>();
         for (ComponentKey key: mCache.keySet()) {
             if (key.componentName.getPackageName().equals(packageName)
                     && key.user.equals(user)) {
@@ -235,7 +221,6 @@
             }
         } catch (NameNotFoundException e) {
             Log.d(TAG, "Package not found", e);
-            return;
         }
     }
 
@@ -280,7 +265,7 @@
             Set<String> ignorePackages) {
         long userSerial = mUserManager.getSerialNumberForUser(user);
         PackageManager pm = mContext.getPackageManager();
-        HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>();
+        HashMap<String, PackageInfo> pkgInfoMap = new HashMap<>();
         for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
             pkgInfoMap.put(info.packageName, info);
         }
@@ -290,7 +275,7 @@
             componentMap.put(app.getComponentName(), app);
         }
 
-        HashSet<Integer> itemsToRemove = new HashSet<Integer>();
+        HashSet<Integer> itemsToRemove = new HashSet<>();
         Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
 
         Cursor c = null;
@@ -387,7 +372,7 @@
         entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
         mCache.put(key, entry);
 
-        Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor);
+        Bitmap lowResIcon = generateLowResIcon(entry.icon);
         ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
                 app.getApplicationInfo().packageName);
         addIconToDB(values, app.getComponentName(), info, userSerial);
@@ -593,7 +578,6 @@
         // For icon caching, do not go through DB. Just update the in-memory entry.
         if (entry == null) {
             entry = new CacheEntry();
-            mCache.put(cacheKey, entry);
         }
         if (!TextUtils.isEmpty(title)) {
             entry.title = title;
@@ -601,6 +585,9 @@
         if (icon != null) {
             entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
         }
+        if (!TextUtils.isEmpty(title) && entry.icon != null) {
+            mCache.put(cacheKey, entry);
+        }
     }
 
     private static ComponentKey getPackageKey(String packageName, UserHandle user) {
@@ -637,7 +624,11 @@
                     // only keep the low resolution icon instead of the larger full-sized icon
                     Bitmap icon = LauncherIcons.createBadgedIconBitmap(
                             appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
-                    Bitmap lowResIcon =  generateLowResIcon(icon, mPackageBgColor);
+                    if (mInstantAppResolver.isInstantApp(appInfo)) {
+                        icon = LauncherIcons.badgeWithDrawable(icon,
+                                mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
+                    }
+                    Bitmap lowResIcon =  generateLowResIcon(icon);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
                     entry.icon = useLowResIcon ? lowResIcon : icon;
@@ -720,7 +711,7 @@
         private final HashMap<String, PackageInfo> mPkgInfoMap;
         private final Stack<LauncherActivityInfo> mAppsToAdd;
         private final Stack<LauncherActivityInfo> mAppsToUpdate;
-        private final HashSet<String> mUpdatedPackages = new HashSet<String>();
+        private final HashSet<String> mUpdatedPackages = new HashSet<>();
 
         @Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
                 Stack<LauncherActivityInfo> appsToAdd,
@@ -769,7 +760,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 13;
+        private final static int DB_VERSION = 17;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -822,24 +813,10 @@
     /**
      * Generates a new low-res icon given a high-res icon.
      */
-    private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) {
-        if (lowResBackgroundColor == Color.TRANSPARENT) {
-            return Bitmap.createScaledBitmap(icon,
-                            icon.getWidth() / LOW_RES_SCALE_FACTOR,
-                            icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
-        } else {
-            Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
-                    icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
-            synchronized (this) {
-                mLowResCanvas.setBitmap(lowResIcon);
-                mLowResCanvas.drawColor(lowResBackgroundColor);
-                mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
-                        new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
-                        mLowResPaint);
-                mLowResCanvas.setBitmap(null);
-            }
-            return lowResIcon;
-        }
+    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) {
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index a5d3990..4dee2b5 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -2,6 +2,7 @@
 
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 
 import java.util.Locale;
 
@@ -17,15 +18,18 @@
     }
 
     public void updateSystemStateString() {
-        mSystemState = Locale.getDefault().toString();
+        mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
     }
 
     public String getIconSystemState(String packageName) {
         return mSystemState;
     }
 
-
-    public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
+    /**
+     * @param flattenDrawable true if the caller does not care about the specification of the
+     *                        original icon as long as the flattened version looks the same.
+     */
+    public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
         return info.getIcon(iconDpi);
     }
 }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 0608fdd..f919dd0 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -42,12 +42,10 @@
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
+    protected void setupUi() {
         // Get the hover color
         mHoverColor = Themes.getColorAccent(getContext());
-
-        setDrawable(R.drawable.ic_info_launcher);
+        setDrawable(R.drawable.ic_info_shadow);
     }
 
     @Override
@@ -67,12 +65,17 @@
 
     public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
             DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
+        if (info instanceof PromiseAppInfo) {
+            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+            launcher.startActivity(promiseAppInfo.getMarketIntent());
+            return true;
+        }
         boolean result = false;
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
         } else if (info instanceof ShortcutInfo) {
-            componentName = ((ShortcutInfo) info).intent.getComponent();
+            componentName = info.getTargetComponent();
         } else if (info instanceof PendingAddItemInfo) {
             componentName = ((PendingAddItemInfo) info).componentName;
         } else if (info instanceof LauncherAppWidgetInfo) {
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 154641c..be76490 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -9,9 +9,6 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
-
 public class InsettableFrameLayout extends FrameLayout implements
     ViewGroup.OnHierarchyChangeListener, Insettable {
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index ce85570..0370777 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -34,6 +34,7 @@
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -59,6 +60,16 @@
 import java.util.Set;
 
 public class InstallShortcutReceiver extends BroadcastReceiver {
+
+    public static final int FLAG_ACTIVITY_PAUSED = 1;
+    public static final int FLAG_LOADER_RUNNING = 2;
+    public static final int FLAG_DRAG_AND_DROP = 4;
+    public static final int FLAG_BULK_ADD = 4;
+
+    // Determines whether to defer installing shortcuts immediately until
+    // processAllPendingInstalls() is called.
+    private static int sInstallQueueDisabledFlags = 0;
+
     private static final String TAG = "InstallShortcutReceiver";
     private static final boolean DBG = false;
 
@@ -151,10 +162,6 @@
         }
     }
 
-    // Determines whether to defer installing shortcuts immediately until
-    // processAllPendingInstalls() is called.
-    private static boolean mUseInstallQueue = false;
-
     public void onReceive(Context context, Intent data) {
         if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
             return;
@@ -207,7 +214,11 @@
 
     public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
         PendingInstallShortcutInfo info = createPendingInfo(context, data);
-        return info == null ? null : (ShortcutInfo) info.getItemInfo();
+        return info == null ? null : (ShortcutInfo) info.getItemInfo().first;
+    }
+
+    public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+        return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
     }
 
     public static void queueShortcut(ShortcutInfoCompat info, Context context) {
@@ -245,27 +256,28 @@
 
     private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        LauncherAppState app = LauncherAppState.getInstance(context);
-        boolean launcherNotLoaded = app.getModel().getCallback() == null;
-
         addToInstallQueue(Utilities.getPrefs(context), info);
-        if (!mUseInstallQueue && !launcherNotLoaded) {
-            flushInstallQueue(context);
-        }
+        flushInstallQueue(context);
     }
 
-    static void enableInstallQueue() {
-        mUseInstallQueue = true;
+    public static void enableInstallQueue(int flag) {
+        sInstallQueueDisabledFlags |= flag;
     }
-    static void disableAndFlushInstallQueue(Context context) {
-        mUseInstallQueue = false;
+    public static void disableAndFlushInstallQueue(int flag, Context context) {
+        sInstallQueueDisabledFlags &= ~flag;
         flushInstallQueue(context);
     }
 
     static void flushInstallQueue(Context context) {
+        LauncherModel model = LauncherAppState.getInstance(context).getModel();
+        boolean launcherNotLoaded = model.getCallback() == null;
+        if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
+            return;
+        }
+
         ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
         if (!items.isEmpty()) {
-            LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems(
+            model.addAndBindAddedWorkspaceItems(
                     new LazyShortcutsProvider(context.getApplicationContext(), items));
         }
     }
@@ -439,7 +451,7 @@
             }
         }
 
-        public ItemInfo getItemInfo() {
+        public Pair<ItemInfo, Object> getItemInfo() {
             if (activityInfo != null) {
                 AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
                 final LauncherAppState app = LauncherAppState.getInstance(mContext);
@@ -459,11 +471,11 @@
                         }
                     });
                 }
-                return si;
+                return Pair.create((ItemInfo) si, (Object) activityInfo);
             } else if (shortcutInfo != null) {
                 ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
                 si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
-                return si;
+                return Pair.create((ItemInfo) si, (Object) shortcutInfo);
             } else if (providerInfo != null) {
                 LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
                         .fromProviderInfo(mContext, providerInfo);
@@ -475,9 +487,10 @@
                 widgetInfo.minSpanY = info.minSpanY;
                 widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
                 widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
-                return widgetInfo;
+                return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
             } else {
-                return createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+                ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+                return Pair.create((ItemInfo) si, null);
             }
         }
 
@@ -588,7 +601,7 @@
         return new PendingInstallShortcutInfo(info, original.mContext);
     }
 
-    private static class LazyShortcutsProvider extends Provider<List<ItemInfo>> {
+    private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> {
 
         private final Context mContext;
         private final ArrayList<PendingInstallShortcutInfo> mPendingItems;
@@ -603,9 +616,9 @@
          * packageManager and icon cache.
          */
         @Override
-        public ArrayList<ItemInfo> get() {
+        public ArrayList<Pair<ItemInfo, Object>> get() {
             Preconditions.assertNonUiThread();
-            ArrayList<ItemInfo> installQueue = new ArrayList<>();
+            ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
             LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
             for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
                 // If the intent specifies a package, make sure the package exists
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9e214d1..7a43198 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,7 +28,6 @@
 import android.view.WindowManager;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.Thunk;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -77,6 +76,7 @@
     public int numFolderRows;
     public int numFolderColumns;
     public float iconSize;
+    public float landscapeIconSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
     public float iconTextSize;
@@ -85,9 +85,9 @@
      * Number of icons inside the hotseat area.
      */
     public int numHotseatIcons;
-    float hotseatIconSize;
-    public float hotseatScale;
+
     int defaultLayoutId;
+    int demoModeLayoutId;
 
     public DeviceProfile landscapeProfile;
     public DeviceProfile portraitProfile;
@@ -100,12 +100,12 @@
     public InvariantDeviceProfile(InvariantDeviceProfile p) {
         this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
                 p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
-                p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize,
-                p.defaultLayoutId);
+                p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
+                p.defaultLayoutId, p.demoModeLayoutId);
     }
 
     InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
-            float is, float its, int hs, float his, int dlId) {
+            float is, float lis, float its, int hs, int dlId, int dmlId) {
         name = n;
         minWidthDps = w;
         minHeightDps = h;
@@ -115,12 +115,11 @@
         numFolderColumns = fc;
         minAllAppsPredictionColumns = maapc;
         iconSize = is;
+        landscapeIconSize = lis;
         iconTextSize = its;
         numHotseatIcons = hs;
-        hotseatIconSize = his;
         defaultLayoutId = dlId;
-
-        hotseatScale = hotseatIconSize / iconSize;
+        demoModeLayoutId = dmlId;
     }
 
     @TargetApi(23)
@@ -148,22 +147,21 @@
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
         defaultLayoutId = closestProfile.defaultLayoutId;
+        demoModeLayoutId = closestProfile.demoModeLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
         minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
 
         iconSize = interpolatedDeviceProfileOut.iconSize;
+        landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
         iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
         iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
-        hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, dm);
 
-        hotseatScale = hotseatIconSize / iconSize;
-
         Point realSize = new Point();
         display.getRealSize(realSize);
         // The real size never changes. smallSide and largeSide will remain the
@@ -211,10 +209,11 @@
                             a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
                             a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
                             iconSize,
+                            a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
                             a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
                             a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
-                            a.getFloat(R.styleable.InvariantDeviceProfile_hotseatIconSize, iconSize),
-                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
+                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
+                            a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
                     a.recycle();
                 }
             }
@@ -305,19 +304,19 @@
 
     private void add(InvariantDeviceProfile p) {
         iconSize += p.iconSize;
+        landscapeIconSize += p.landscapeIconSize;
         iconTextSize += p.iconTextSize;
-        hotseatIconSize += p.hotseatIconSize;
     }
 
     private InvariantDeviceProfile multiply(float w) {
         iconSize *= w;
+        landscapeIconSize *= w;
         iconTextSize *= w;
-        hotseatIconSize *= w;
         return this;
     }
 
     public int getAllAppsButtonRank() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+        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;
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 0779a3d..fa3253c 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -39,8 +39,10 @@
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
      * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER}, or
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER},
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET} or
+     * {@link LauncherSettings.Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
      */
     public int itemType;
 
@@ -53,7 +55,9 @@
     public long container = NO_ID;
 
     /**
-     * Indicates the screen in which the shortcut appears.
+     * 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;
 
@@ -133,7 +137,12 @@
     }
 
     public ComponentName getTargetComponent() {
-        return getIntent() == null ? null : getIntent().getComponent();
+        Intent intent = getIntent();
+        if (intent != null) {
+            return intent.getComponent();
+        } else {
+            return null;
+        }
     }
 
     public void writeToValues(ContentWriter writer) {
@@ -178,15 +187,12 @@
 
     protected String dumpProperties() {
         return "id=" + id
-                + " type=" + itemType
-                + " container=" + container
+                + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
+                + " container=" + LauncherSettings.Favorites.containerToString((int)container)
                 + " screen=" + screenId
-                + " cellX=" + cellX
-                + " cellY=" + cellY
-                + " spanX=" + spanX
-                + " spanY=" + spanY
-                + " minSpanX=" + minSpanX
-                + " minSpanY=" + minSpanY
+                + " cell(" + cellX + "," + cellY + ")"
+                + " span(" + spanX + "," + spanY + ")"
+                + " minSpan(" + minSpanX + "," + minSpanY + ")"
                 + " rank=" + rank
                 + " user=" + user
                 + " title=" + title;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2de1003..32af059 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,7 +18,9 @@
 
 import android.Manifest;
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
@@ -65,6 +67,7 @@
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MotionEvent;
 import android.view.View;
@@ -76,27 +79,27 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.OvershootInterpolator;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.anim.AnimationLayerSet;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 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.dragndrop.PinItemDragListener;
 import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -111,19 +114,22 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutKey;
 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.ControlType;
 import com.android.launcher3.util.ActivityResultInfo;
+import com.android.launcher3.util.RunnableWithId;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.TestingUtils;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -132,6 +138,7 @@
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetsContainerView;
 
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -140,6 +147,10 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
+
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
 
 /**
  * Default launcher application.
@@ -147,7 +158,8 @@
 public class Launcher extends BaseActivity
         implements LauncherExterns, View.OnClickListener, OnLongClickListener,
                    LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
-                   AccessibilityManager.AccessibilityStateChangeListener {
+                   AccessibilityManager.AccessibilityStateChangeListener,
+                   WallpaperColorInfo.OnThemeChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -157,14 +169,15 @@
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
+
     private static final int REQUEST_PICK_APPWIDGET = 9;
     private static final int REQUEST_PICK_WALLPAPER = 10;
 
     private static final int REQUEST_BIND_APPWIDGET = 11;
-    private static final int REQUEST_BIND_PENDING_APPWIDGET = 14;
-    private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
+    private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+    private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
 
-    private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
+    private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
 
     private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
 
@@ -203,28 +216,27 @@
 
     private boolean mIsSafeModeEnabled;
 
-    public static final int APPWIDGET_HOST_ID = 1024;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
-    private static final int ACTIVITY_START_DELAY = 1000;
 
     // How long to wait before the new-shortcut animation automatically pans the workspace
-    private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
-    private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
-    @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
+    private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+    private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
+    @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+
+    private final ExtractedColors mExtractedColors = new ExtractedColors();
 
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
     @Thunk DragLayer mDragLayer;
     private DragController mDragController;
-    private View mQsbContainer;
 
     public View mWeightWatcher;
 
     private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    private int[] mTmpAddItemCellCoordinates = new int[2];
+    private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
     private ViewGroup mOverviewPanel;
@@ -240,7 +252,10 @@
 
     // Main container view and the model for the widget tray screen.
     @Thunk WidgetsContainerView mWidgetsView;
-    @Thunk MultiHashMap<PackageItemInfo, WidgetItem> mAllWidgets;
+
+    // We need to store the orientation Launcher was created with, due to a bug (b/64916689)
+    // that results in widgets being inflated in the wrong orientation.
+    private int mOrientation;
 
     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
     // scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -254,29 +269,27 @@
     private boolean mPaused = true;
     private boolean mOnResumeNeedsLoad;
 
-    private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
-    private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
+    private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
+    private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
     private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
     private ModelWriter mModelWriter;
     private IconCache mIconCache;
-    private ExtractedColors mExtractedColors;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
-    private Handler mHandler = new Handler();
-    private boolean mIsResumeFromActionScreenOff;
+    private final Handler mHandler = new Handler();
     private boolean mHasFocus = false;
-    private boolean mAttached = false;
+
+    private ObjectAnimator mScrimAnimator;
+    private boolean mShouldFadeInScrim;
 
     private PopupDataProvider mPopupDataProvider;
 
-    private View.OnTouchListener mHapticFeedbackTouchListener;
-
     // Determines how long to wait after a rotation before restoring the screen orientation to
     // match the sensor state.
     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
-    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -289,8 +302,8 @@
     // the press state and keep this reference to reset the press state when we return to launcher.
     private BubbleTextView mWaitingForResume;
 
-    protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
-            new HashMap<String, CustomAppWidget>();
+    protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
+        new HashMap<>();
 
     static {
         if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
@@ -303,7 +316,7 @@
     // simply unregister this runnable.
     private Runnable mExitSpringLoadedModeRunnable;
 
-    @Thunk Runnable mBuildLayersRunnable = new Runnable() {
+    @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
                 mWorkspace.buildPageHardwareLayers();
@@ -359,6 +372,10 @@
             mLauncherCallbacks.preOnCreate();
         }
 
+        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+        wallpaperColorInfo.setOnThemeChangeListener(this);
+        overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
+
         super.onCreate(savedInstanceState);
 
         LauncherAppState app = LauncherAppState.getInstance(this);
@@ -372,6 +389,7 @@
             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
         }
 
+        mOrientation = getResources().getConfiguration().orientation;
         mSharedPrefs = Utilities.getPrefs(this);
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
@@ -385,7 +403,10 @@
 
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
-        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
+        mAppWidgetHost = new LauncherAppWidgetHost(this);
+        if (Utilities.ATLEAST_MARSHMALLOW) {
+            mAppWidgetHost.addProviderChangeListener(this);
+        }
         mAppWidgetHost.startListening();
 
         // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
@@ -393,11 +414,10 @@
         // LauncherModel load.
         mPaused = false;
 
-        mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
+        mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
 
         setupViews();
         mDeviceProfile.layout(this, false /* notifyListeners */);
-        mExtractedColors = new ExtractedColors();
         loadExtractedColorsAndColorItems();
 
         mPopupDataProvider = new PopupDataProvider(this);
@@ -453,19 +473,48 @@
         setOrientation();
 
         setContentView(mLauncherView);
+
+        // Listen for broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
+        registerReceiver(mReceiver, filter);
+        mShouldFadeInScrim = true;
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onCreate(savedInstanceState);
         }
     }
 
     @Override
-    public View findViewById(int id) {
+    public void onThemeChanged() {
+        recreate();
+    }
+
+    protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
+        if (isDark) {
+            setTheme(R.style.LauncherThemeDark);
+        } else if (supportsDarkText) {
+            setTheme(R.style.LauncherThemeDarkText);
+        }
+    }
+
+    @Override
+    public <T extends View> T findViewById(int id) {
         return mLauncherView.findViewById(id);
     }
 
     @Override
     public void onExtractedColorsChanged() {
         loadExtractedColorsAndColorItems();
+        mExtractedColors.notifyChange();
+    }
+
+    public ExtractedColors getExtractedColors() {
+        return mExtractedColors;
     }
 
     @Override
@@ -481,46 +530,6 @@
             mExtractedColors.load(this);
             mHotseat.updateColor(mExtractedColors, !mPaused);
             mWorkspace.getPageIndicator().updateColor(mExtractedColors);
-            boolean lightStatusBar = (FeatureFlags.LIGHT_STATUS_BAR
-                    && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
-                    ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
-            // It's possible that All Apps is visible when this is run,
-            // so always use light status bar in that case. Only change nav bar color to status bar
-            // color when All Apps is visible.
-            activateLightSystemBars(lightStatusBar || isAllAppsVisible(), true, isAllAppsVisible());
-        }
-    }
-
-    // TODO: use platform flag on API >= 26
-    private static final int SYSTEM_UI_FLAG_LIGHT_NAV_BAR = 0x10;
-
-    /**
-     * Sets the status and/or nav bar to be light or not. Light status bar means dark icons.
-     * @param isLight make sure the system bar is light.
-     * @param statusBar if true, make the status bar theme match the isLight param.
-     * @param navBar if true, make the nav bar theme match the isLight param.
-     */
-    public void activateLightSystemBars(boolean isLight, boolean statusBar, boolean navBar) {
-        int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
-        int newSystemUiFlags = oldSystemUiFlags;
-        if (isLight) {
-            if (statusBar) {
-                newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-            }
-            if (navBar && Utilities.isAtLeastO()) {
-                newSystemUiFlags |= SYSTEM_UI_FLAG_LIGHT_NAV_BAR;
-            }
-        } else {
-            if (statusBar) {
-                newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-            }
-            if (navBar && Utilities.isAtLeastO()) {
-                newSystemUiFlags &= ~(SYSTEM_UI_FLAG_LIGHT_NAV_BAR);
-            }
-        }
-
-        if (newSystemUiFlags != oldSystemUiFlags) {
-            getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
         }
     }
 
@@ -550,47 +559,6 @@
 
     public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
         mLauncherCallbacks = callbacks;
-        mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
-            private boolean mWorkspaceImportanceStored = false;
-            private boolean mHotseatImportanceStored = false;
-            private int mWorkspaceImportanceForAccessibility =
-                    View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-            private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-
-            @Override
-            public void onSearchOverlayOpened() {
-                if (mWorkspaceImportanceStored || mHotseatImportanceStored) {
-                    return;
-                }
-                // The underlying workspace and hotseat are temporarily suppressed by the search
-                // overlay. So they shouldn't be accessible.
-                if (mWorkspace != null) {
-                    mWorkspaceImportanceForAccessibility =
-                            mWorkspace.getImportantForAccessibility();
-                    mWorkspace.setImportantForAccessibility(
-                            View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-                    mWorkspaceImportanceStored = true;
-                }
-                if (mHotseat != null) {
-                    mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility();
-                    mHotseat.setImportantForAccessibility(
-                            View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-                    mHotseatImportanceStored = true;
-                }
-            }
-
-            @Override
-            public void onSearchOverlayClosed() {
-                if (mWorkspaceImportanceStored && mWorkspace != null) {
-                    mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility);
-                }
-                if (mHotseatImportanceStored && mHotseat != null) {
-                    mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility);
-                }
-                mWorkspaceImportanceStored = false;
-                mHotseatImportanceStored = false;
-            }
-        });
         return true;
     }
 
@@ -830,7 +798,7 @@
     }
 
     @Override
-    protected void onActivityResult(
+    public void onActivityResult(
             final int requestCode, final int resultCode, final Intent data) {
         handleActivityResult(requestCode, resultCode, data);
         if (mLauncherCallbacks != null) {
@@ -952,6 +920,25 @@
         if (!isWorkspaceLoading()) {
             NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
         }
+
+        if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
+            if (mScrimAnimator != null) {
+                mScrimAnimator.cancel();
+            }
+            mDragLayer.getBackground().setAlpha(0);
+            mScrimAnimator = ObjectAnimator.ofInt(mDragLayer.getBackground(),
+                    LauncherAnimUtils.DRAWABLE_ALPHA, 0, 255);
+            mScrimAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mScrimAnimator = null;
+                }
+            });
+            mScrimAnimator.setDuration(600);
+            mScrimAnimator.setStartDelay(getWindow().getTransitionBackgroundFadeDuration());
+            mScrimAnimator.start();
+        }
+        mShouldFadeInScrim = false;
     }
 
     @Override
@@ -977,11 +964,13 @@
             // Don't update the predicted apps if the user is returning to launcher in the apps
             // view after launching an app, as they may be depending on the UI to be static to
             // switch to another app, otherwise, if it was
-            showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
-                    mAppsView.shouldRestoreImeState() /* focusSearchBar */);
+            showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
         } else if (mOnResumeState == State.WIDGETS) {
             showWidgetsView(false, false);
         }
+        if (mOnResumeState != State.APPS) {
+            tryAndUpdatePredictedApps();
+        }
         mOnResumeState = State.NONE;
 
         mPaused = false;
@@ -1051,18 +1040,16 @@
         updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
         mWorkspace.onResume();
 
-        if (!isWorkspaceLoading()) {
-            // Process any items that were added while Launcher was away.
-            InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+        // 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();
-        }
+        // Refresh shortcuts if the permission changed.
+        mModel.refreshShortcutsIfRequired();
 
         if (shouldShowDiscoveryBounce()) {
             mAllAppsController.showDiscoveryBounce();
         }
-        mIsResumeFromActionScreenOff = false;
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
@@ -1072,7 +1059,7 @@
     @Override
     protected void onPause() {
         // Ensure that items added to Launcher are queued until Launcher returns
-        InstallShortcutReceiver.enableInstallQueue();
+        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
 
         super.onPause();
         mPaused = true;
@@ -1093,13 +1080,13 @@
     public interface CustomContentCallbacks {
         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
         // by a onResume or by scrolling otherwise.
-        public void onShow(boolean fromResume);
+        void onShow(boolean fromResume);
 
         // Custom content is completely hidden
-        public void onHide();
+        void onHide();
 
         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
-        public void onScrollProgressChanged(float progress);
+        void onScrollProgressChanged(float progress);
 
         // Indicates whether the user is allowed to scroll away from the custom content.
         boolean isScrollingAllowed();
@@ -1110,40 +1097,28 @@
         /**
          * Touch interaction leading to overscroll has begun
          */
-        public void onScrollInteractionBegin();
+        void onScrollInteractionBegin();
 
         /**
          * Touch interaction related to overscroll has ended
          */
-        public void onScrollInteractionEnd();
+        void onScrollInteractionEnd();
 
         /**
          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
          * screen (or in the case of RTL, the rightmost screen).
          */
-        public void onScrollChange(float progress, boolean rtl);
+        void onScrollChange(float progress, boolean rtl);
 
         /**
          * Called when the launcher is ready to use the overlay
          * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
          */
-        public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
-    }
-
-    public interface LauncherSearchCallbacks {
-        /**
-         * Called when the search overlay is shown.
-         */
-        public void onSearchOverlayOpened();
-
-        /**
-         * Called when the search overlay is dismissed.
-         */
-        public void onSearchOverlayClosed();
+        void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
     }
 
     public interface LauncherOverlayCallbacks {
-        public void onScrollChanged(float progress);
+        void onScrollChanged(float progress);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1162,7 +1137,7 @@
             // 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.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
+            return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
         }
     }
 
@@ -1297,9 +1272,7 @@
     private void setupViews() {
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mFocusHandler = mDragLayer.getFocusIndicatorHelper();
-        mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mQsbContainer = mDragLayer.findViewById(mDeviceProfile.isVerticalBarLayout()
-                ? R.id.workspace_blocked_row : R.id.qsb_container);
+        mWorkspace = mDragLayer.findViewById(R.id.workspace);
         mWorkspace.initParentViews(mDragLayer);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1329,25 +1302,18 @@
         mDragController.addDragListener(mWorkspace);
 
         // Get the search/delete/uninstall bar
-        mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
+        mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
 
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
         mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
-        if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
-            mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
-        } else {
-            mAppsView.setSearchBarController(new DefaultAppSearchController());
-        }
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         mDragController.setMoveTarget(mWorkspace);
         mDragController.addDropTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
-        }
+        mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
 
         if (TestingUtils.MEMORY_DUMP_ENABLED) {
             TestingUtils.addWeightWatcher(this);
@@ -1365,7 +1331,6 @@
                 onClickWallpaperPicker(view);
             }
         }.attachTo(wallpaperButton);
-        wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
 
         // Bind widget button actions
         mWidgetsButton = findViewById(R.id.widget_button);
@@ -1375,7 +1340,6 @@
                 onClickAddWidgetButton(view);
             }
         }.attachTo(mWidgetsButton);
-        mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
 
         // Bind settings actions
         View settingsButton = findViewById(R.id.settings_button);
@@ -1387,7 +1351,6 @@
                     onClickSettingsButton(view);
                 }
             }.attachTo(settingsButton);
-            settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
         } else {
             settingsButton.setVisibility(View.GONE);
         }
@@ -1429,34 +1392,33 @@
      * @return A View inflated from layoutResId.
      */
     public View createShortcut(ViewGroup parent, ShortcutInfo info) {
-        BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
-                parent, false);
+        BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.app_icon, parent, false);
         favorite.applyFromShortcutInfo(info);
-        favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
         favorite.setOnClickListener(this);
         favorite.setOnFocusChangeListener(mFocusHandler);
         return favorite;
     }
 
     /**
-     * Add a shortcut to the workspace.
+     * Add a shortcut to the workspace or to a Folder.
      *
      * @param data The intent describing the shortcut.
      */
     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
             int cellY, PendingRequestArgs args) {
-        int[] cellXY = mTmpAddItemCellCoordinates;
-        CellLayout layout = getCellLayout(container, screenId);
-
-        if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
-                args.getPendingIntent().getComponent() == null) {
+        if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+                || args.getPendingIntent().getComponent() == null) {
             return;
         }
 
+        int[] cellXY = mTmpAddItemCellCoordinates;
+        CellLayout layout = getCellLayout(container, screenId);
+
         ShortcutInfo info = null;
-        if (Utilities.isAtLeastO()) {
-            info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
-                    this, PinItemRequestCompat.getPinItemRequest(data), 0);
+        if (Utilities.ATLEAST_OREO) {
+            info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+                    this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
         }
 
         if (info == null) {
@@ -1475,36 +1437,57 @@
             }
         }
 
-        final View view = createShortcut(info);
-        boolean foundCellSpan = false;
-        // First we check if we already know the exact location where we want to add this item.
-        if (cellX >= 0 && cellY >= 0) {
-            cellXY[0] = cellX;
-            cellXY[1] = cellY;
-            foundCellSpan = true;
+        if (container < 0) {
+            // Adding a shortcut to the Workspace.
+            final View view = createShortcut(info);
+            boolean foundCellSpan = false;
+            // First we check if we already know the exact location where we want to add this item.
+            if (cellX >= 0 && cellY >= 0) {
+                cellXY[0] = cellX;
+                cellXY[1] = cellY;
+                foundCellSpan = true;
 
-            // If appropriate, either create a folder or add to an existing folder
-            if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
-                    true, null,null)) {
+                // If appropriate, either create a folder or add to an existing folder
+                if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
+                        true, null, null)) {
+                    return;
+                }
+                DragObject dragObject = new DragObject();
+                dragObject.dragInfo = info;
+                if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
+                        true)) {
+                    return;
+                }
+            } else {
+                foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+            }
+
+            if (!foundCellSpan) {
+                mWorkspace.onNoCellFound(layout);
                 return;
             }
-            DragObject dragObject = new DragObject();
-            dragObject.dragInfo = info;
-            if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
-                    true)) {
-                return;
-            }
+
+            getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
+            mWorkspace.addInScreen(view, info);
         } else {
-            foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+            // Adding a shortcut to a Folder.
+            FolderIcon folderIcon = findFolderIcon(container);
+            if (folderIcon != null) {
+                FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
+                folderInfo.add(info, args.rank, false);
+            } else {
+                Log.e(TAG, "Could not find folder with id " + container + " to add shortcut.");
+            }
         }
+    }
 
-        if (!foundCellSpan) {
-            mWorkspace.onNoCellFound(layout);
-            return;
-        }
-
-        getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
-        mWorkspace.addInScreen(view, info);
+    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;
+            }
+        });
     }
 
     /**
@@ -1565,7 +1548,11 @@
                         mAppsView.reset();
                     }
                 }
-                mIsResumeFromActionScreenOff = true;
+                mShouldFadeInScrim = true;
+            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+                // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
+                // the user unlocked and the Launcher is not in the foreground.
+                mShouldFadeInScrim = false;
             }
         }
     };
@@ -1592,13 +1579,7 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        // Listen for broadcasts related to user-presence
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        registerReceiver(mReceiver, filter);
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
-        mAttached = true;
-
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onAttachedToWindow();
         }
@@ -1607,10 +1588,6 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mAttached) {
-            unregisterReceiver(mReceiver);
-            mAttached = false;
-        }
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onDetachedFromWindow();
@@ -1648,7 +1625,6 @@
                                 }
                             }
                         });
-                        return;
                     }
                 });
             }
@@ -1672,10 +1648,6 @@
         return mWorkspace;
     }
 
-    public View getQsbContainer() {
-        return mQsbContainer;
-    }
-
     public Hotseat getHotseat() {
         return mHotseat;
     }
@@ -1704,6 +1676,8 @@
         return mSharedPrefs;
     }
 
+    public int getOrientation() { return mOrientation; }
+
     @Override
     protected void onNewIntent(Intent intent) {
         long startTime = 0;
@@ -1767,7 +1741,7 @@
 
             // Reset the apps view
             if (!alreadyOnHome && mAppsView != null) {
-                mAppsView.scrollToTop();
+                mAppsView.reset();
             }
 
             // Reset the widgets view
@@ -1789,8 +1763,9 @@
         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
         // animation.
         if (isActionMain) {
-            boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
-                    mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+            boolean callbackAllowsMoveToDefaultScreen =
+                mLauncherCallbacks == null || mLauncherCallbacks
+                    .shouldMoveToDefaultScreenOnHomeIntent();
             if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
                     && callbackAllowsMoveToDefaultScreen) {
 
@@ -1851,6 +1826,7 @@
     public void onDestroy() {
         super.onDestroy();
 
+        unregisterReceiver(mReceiver);
         mWorkspace.removeCallbacks(mBuildLayersRunnable);
         mWorkspace.removeFolderListeners();
 
@@ -1878,8 +1854,12 @@
         ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
                 .removeAccessibilityStateChangeListener(this);
 
+        WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
+
         LauncherAnimUtils.onDestroyActivity();
 
+        clearPendingBinds();
+
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onDestroy();
         }
@@ -2299,8 +2279,8 @@
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
-        } else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
-                (v == mAllAppsButton && mAllAppsButton != null)) {
+        } else if ((v instanceof PageIndicator) ||
+            (v == mAllAppsButton && mAllAppsButton != null)) {
             onClickAllAppsButton(v);
         } else if (tag instanceof AppInfo) {
             startAppShortcutOrInfoActivity(v);
@@ -2351,8 +2331,9 @@
     }
 
     /**
-     * Event handler for the "grid" button that appears on the home screen, which
-     * enters all apps mode.
+     * Event handler for the "grid" button or "caret" that appears on the home screen, which
+     * enters all apps mode. In verticalBarLayout the caret can be seen when all apps is open, and
+     * so in that case reverses the action.
      *
      * @param v The view that was clicked.
      */
@@ -2361,18 +2342,9 @@
         if (!isAppsViewVisible()) {
             getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                     ControlType.ALL_APPS_BUTTON);
-            showAppsView(true /* animated */, true /* updatePredictedApps */,
-                    false /* focusSearchBar */);
-        }
-    }
-
-    protected void onLongClickAllAppsButton(View v) {
-        if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
-        if (!isAppsViewVisible()) {
-            getUserEventDispatcher().logActionOnControl(Action.Touch.LONGPRESS,
-                    ControlType.ALL_APPS_BUTTON);
-            showAppsView(true /* animated */,
-                    true /* updatePredictedApps */, true /* focusSearchBar */);
+            showAppsView(true /* animated */, true /* updatePredictedApps */);
+        } else {
+            showWorkspace(true);
         }
     }
 
@@ -2453,7 +2425,7 @@
         }
 
         // Check for abandoned promise
-        if ((v instanceof BubbleTextView) && shortcut.isPromise()) {
+        if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
             String packageName = shortcut.intent.getComponent() != null ?
                     shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
             if (!TextUtils.isEmpty(packageName)) {
@@ -2469,7 +2441,13 @@
 
     private void startAppShortcutOrInfoActivity(View v) {
         ItemInfo item = (ItemInfo) v.getTag();
-        Intent intent = item.getIntent();
+        Intent intent;
+        if (item instanceof PromiseAppInfo) {
+            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+            intent = promiseAppInfo.getMarketIntent();
+        } else {
+            intent = item.getIntent();
+        }
         if (intent == null) {
             throw new IllegalArgumentException("Input must have a valid intent");
         }
@@ -2530,8 +2508,8 @@
                 .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
 
         String pickerPackage = getString(R.string.wallpaper_picker_package);
-        boolean hasTargetPackage = TextUtils.isEmpty(pickerPackage);
-        if (!hasTargetPackage) {
+        boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
+        if (hasTargetPackage) {
             intent.setPackage(pickerPackage);
         }
 
@@ -2559,22 +2537,6 @@
         startActivity(intent, getActivityLaunchOptions(v));
     }
 
-    public View.OnTouchListener getHapticFeedbackTouchListener() {
-        if (mHapticFeedbackTouchListener == null) {
-            mHapticFeedbackTouchListener = new View.OnTouchListener() {
-                @SuppressLint("ClickableViewAccessibility")
-                @Override
-                public boolean onTouch(View v, MotionEvent event) {
-                    if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
-                        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-                    }
-                    return false;
-                }
-            };
-        }
-        return mHapticFeedbackTouchListener;
-    }
-
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         mDragLayer.onAccessibilityStateChanged(enabled);
@@ -2673,9 +2635,9 @@
         if (Utilities.ATLEAST_MARSHMALLOW) {
             int left = 0, top = 0;
             int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-            if (v instanceof TextView) {
+            if (v instanceof BubbleTextView) {
                 // Launch from center of icon, not entire view
-                Drawable icon = Workspace.getTextViewIcon((TextView) v);
+                Drawable icon = ((BubbleTextView) v).getIcon();
                 if (icon != null) {
                     Rect bounds = icon.getBounds();
                     left = (width - bounds.width()) / 2;
@@ -2754,13 +2716,6 @@
         if (isWorkspaceLocked()) return false;
         if (mState != State.WORKSPACE) return false;
 
-        if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
-                (v == mAllAppsButton && mAllAppsButton != null)) {
-            onLongClickAllAppsButton(v);
-            return true;
-        }
-
-
         boolean ignoreLongPressToOverview =
                 mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
 
@@ -2960,13 +2915,12 @@
     /**
      * Shows the apps view.
      */
-    public void showAppsView(boolean animated, boolean updatePredictedApps,
-            boolean focusSearchBar) {
+    public void showAppsView(boolean animated, boolean updatePredictedApps) {
         markAppsViewShown();
         if (updatePredictedApps) {
             tryAndUpdatePredictedApps();
         }
-        showAppsOrWidgets(State.APPS, animated, focusSearchBar);
+        showAppsOrWidgets(State.APPS, animated);
     }
 
     /**
@@ -2977,7 +2931,7 @@
         if (resetPageToZero) {
             mWidgetsView.scrollToTop();
         }
-        showAppsOrWidgets(State.WIDGETS, animated, false);
+        showAppsOrWidgets(State.WIDGETS, animated);
 
         mWidgetsView.post(new Runnable() {
             @Override
@@ -2994,7 +2948,7 @@
      */
     // TODO: calling method should use the return value so that when {@code false} is returned
     // the workspace transition doesn't fall into invalid state.
-    private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
+    private boolean showAppsOrWidgets(State toState, boolean animated) {
         if (!(mState == State.WORKSPACE ||
                 mState == State.APPS_SPRING_LOADED ||
                 mState == State.WIDGETS_SPRING_LOADED ||
@@ -3012,7 +2966,7 @@
         }
 
         if (toState == State.APPS) {
-            mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+            mStateTransitionAnimation.startAnimationToAllApps(animated);
         } else {
             mStateTransitionAnimation.startAnimationToWidgets(animated);
         }
@@ -3085,8 +3039,7 @@
 
     public void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
-            showAppsView(true /* animated */,
-                    false /* updatePredictedApps */, false /* focusSearchBar */);
+            showAppsView(true /* animated */, false /* updatePredictedApps */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
         } else if (mState == State.WORKSPACE_SPRING_LOADED) {
@@ -3100,10 +3053,9 @@
      */
     public void tryAndUpdatePredictedApps() {
         if (mLauncherCallbacks != null) {
-            List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
+            List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
                 mAppsView.setPredictedApps(apps);
-                getUserEventDispatcher().setPredictedApps(apps);
             }
         }
     }
@@ -3147,12 +3099,12 @@
      *
      * @return {@code true} if we are currently paused. The caller might be able to skip some work
      */
-    @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
+    @Thunk boolean waitUntilResume(Runnable run) {
         if (mPaused) {
             if (LOGD) Log.d(TAG, "Deferring update until onResume");
-            if (deletePreviousRunnables) {
-                while (mBindOnResumeCallbacks.remove(run)) {
-                }
+            if (run instanceof RunnableWithId) {
+                // Remove any runnables which have the same id
+                while (mBindOnResumeCallbacks.remove(run)) { }
             }
             mBindOnResumeCallbacks.add(run);
             return true;
@@ -3161,10 +3113,6 @@
         }
     }
 
-    private boolean waitUntilResume(Runnable run) {
-        return waitUntilResume(run, false);
-    }
-
     public void addOnResumeCallback(Runnable run) {
         mOnResumeCallbacks.add(run);
     }
@@ -3252,7 +3200,7 @@
                 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
             orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
-            mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+            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();
@@ -3283,13 +3231,13 @@
         }
     }
 
+    @Override
     public void bindAppsAdded(final ArrayList<Long> newScreens,
                               final ArrayList<ItemInfo> addNotAnimated,
-                              final ArrayList<ItemInfo> addAnimated,
-                              final ArrayList<AppInfo> addedApps) {
+                              final ArrayList<ItemInfo> addAnimated) {
         Runnable r = new Runnable() {
             public void run() {
-                bindAppsAdded(newScreens, addNotAnimated, addAnimated, addedApps);
+                bindAppsAdded(newScreens, addNotAnimated, addAnimated);
             }
         };
         if (waitUntilResume(r)) {
@@ -3304,20 +3252,14 @@
         // We add the items without animation on non-visible pages, and with
         // animations on the new page (which we will try and snap to).
         if (addNotAnimated != null && !addNotAnimated.isEmpty()) {
-            bindItems(addNotAnimated, 0,
-                    addNotAnimated.size(), false);
+            bindItems(addNotAnimated, false);
         }
         if (addAnimated != null && !addAnimated.isEmpty()) {
-            bindItems(addAnimated, 0,
-                    addAnimated.size(), true);
+            bindItems(addAnimated, true);
         }
 
         // Remove the extra empty screen
         mWorkspace.removeExtraEmptyScreen(false, false);
-
-        if (addedApps != null && mAppsView != null) {
-            mAppsView.addApps(addedApps);
-        }
     }
 
     /**
@@ -3326,11 +3268,10 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     @Override
-    public void bindItems(final ArrayList<ItemInfo> items, final int start, final int end,
-                          final boolean forceAnimateIcons) {
+    public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
         Runnable r = new Runnable() {
             public void run() {
-                bindItems(items, start, end, forceAnimateIcons);
+                bindItems(items, forceAnimateIcons);
             }
         };
         if (waitUntilResume(r)) {
@@ -3339,11 +3280,12 @@
 
         // 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<Animator>();
+        final Collection<Animator> bounceAnims = new ArrayList<>();
         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
         Workspace workspace = mWorkspace;
         long newItemsScreenId = -1;
-        for (int i = start; i < end; i++) {
+        int end = items.size();
+        for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
 
             // Short circuit if we are loading dock items for a configuration which has no dock
@@ -3368,19 +3310,10 @@
                     break;
                 }
                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
-                    LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
-                    if (mIsSafeModeEnabled) {
-                        view = new PendingAppWidgetHostView(this, info, mIconCache, true);
-                    } else {
-                        LauncherAppWidgetProviderInfo providerInfo =
-                                mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId);
-                        if (providerInfo == null) {
-                            deleteWidgetInfo(info);
-                            continue;
-                        }
-                        view = mAppWidgetHost.createView(this, info.appWidgetId, providerInfo);
+                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
+                    if (view == null) {
+                        continue;
                     }
-                    prepareAppWidget((AppWidgetHostView) view, info);
                     break;
                 }
                 default:
@@ -3397,7 +3330,7 @@
                     Object tag = v.getTag();
                     String desc = "Collision while binding workspace item: " + item
                             + ". Collides with " + tag;
-                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                    if (FeatureFlags.IS_DOGFOOD_BUILD) {
                         throw (new RuntimeException(desc));
                     } else {
                         Log.d(TAG, desc);
@@ -3450,26 +3383,21 @@
 
     /**
      * Add the views for a widget to the workspace.
-     *
-     * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAppWidget(final LauncherAppWidgetInfo item) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindAppWidget(item);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
+    public void bindAppWidget(LauncherAppWidgetInfo item) {
+        View view = inflateAppWidget(item);
+        if (view != null) {
+            mWorkspace.addInScreen(view, item);
+            mWorkspace.requestLayout();
         }
+    }
 
+    private View inflateAppWidget(LauncherAppWidgetInfo item) {
         if (mIsSafeModeEnabled) {
             PendingAppWidgetHostView view =
                     new PendingAppWidgetHostView(this, item, mIconCache, true);
             prepareAppWidget(view, item);
-            mWorkspace.addInScreen(view, item);
-            mWorkspace.requestLayout();
-            return;
+            return view;
         }
 
         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
@@ -3499,7 +3427,7 @@
                             + ", as the provider is null");
                 }
                 getModelWriter().deleteItemFromDatabase(item);
-                return;
+                return null;
             }
 
             // If we do not have a valid id, try to bind an id.
@@ -3567,7 +3495,7 @@
             if (appWidgetInfo == null) {
                 FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                 deleteWidgetInfo(item);
-                return;
+                return null;
             }
 
             item.minSpanX = appWidgetInfo.minSpanX;
@@ -3577,13 +3505,12 @@
             view = new PendingAppWidgetHostView(this, item, mIconCache, false);
         }
         prepareAppWidget(view, item);
-        mWorkspace.addInScreen(view, item);
-        mWorkspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
                     + (SystemClock.uptimeMillis()-start) + "ms");
         }
+        return view;
     }
 
     /**
@@ -3600,6 +3527,9 @@
 
         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
         info.restoreStatus = finalRestoreFlag;
+        if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+            info.pendingItemInfo = null;
+        }
 
         mWorkspace.reinflateWidgetsIfNecessary();
         getModelWriter().updateItemInDatabase(info);
@@ -3678,7 +3608,8 @@
             mPendingActivityResult = null;
         }
 
-        InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+        InstallShortcutReceiver.disableAndFlushInstallQueue(
+                InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
 
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
@@ -3715,29 +3646,28 @@
     }
 
     /**
-     * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
-     * multiple calls to bind the same list.)
-     */
-    @Thunk ArrayList<AppInfo> mTmpAppsList;
-    private Runnable mBindAllApplicationsRunnable = new Runnable() {
-        public void run() {
-            bindAllApplications(mTmpAppsList);
-            mTmpAppsList = null;
-        }
-    };
-
-    /**
      * Add the icons for all apps.
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindAllApplications(final ArrayList<AppInfo> apps) {
-        if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
-            mTmpAppsList = apps;
+        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
+            public void run() {
+                bindAllApplications(apps);
+            }
+        };
+        if (waitUntilResume(r)) {
             return;
         }
 
         if (mAppsView != null) {
+            Executor pendingExecutor = getPendingExecutor();
+            if (pendingExecutor != null && mState != State.APPS) {
+                // Wait until the fade in animation has finished before setting all apps list.
+                pendingExecutor.execute(r);
+                return;
+            }
+
             mAppsView.setApps(apps);
         }
         if (mLauncherCallbacks != null) {
@@ -3746,6 +3676,14 @@
     }
 
     /**
+     * Returns an Executor that will run after the launcher is first drawn (including after the
+     * initial fade in animation). Returns null if the first draw has already occurred.
+     */
+    public @Nullable Executor getPendingExecutor() {
+        return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
+    }
+
+    /**
      * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
      */
@@ -3759,10 +3697,10 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAppsUpdated(final ArrayList<AppInfo> apps) {
+    public void bindAppsAddedOrUpdated(final ArrayList<AppInfo> apps) {
         Runnable r = new Runnable() {
             public void run() {
-                bindAppsUpdated(apps);
+                bindAppsAddedOrUpdated(apps);
             }
         };
         if (waitUntilResume(r)) {
@@ -3770,7 +3708,23 @@
         }
 
         if (mAppsView != null) {
-            mAppsView.updateApps(apps);
+            mAppsView.addOrUpdateApps(apps);
+        }
+    }
+
+    @Override
+    public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
+        Runnable r = new Runnable() {
+            public void run() {
+                bindPromiseAppProgressUpdated(app);
+            }
+        };
+        if (waitUntilResume(r)) {
+            return;
+        }
+
+        if (mAppsView != null) {
+            mAppsView.updatePromiseAppProgress(app);
         }
     }
 
@@ -3792,16 +3746,12 @@
      * Implementation of the method from LauncherModel.Callbacks.
      *
      * @param updated list of shortcuts which have changed.
-     * @param removed list of shortcuts which were deleted in the background. This can happen when
-     *                an app gets removed from the system or some of its components are no longer
-     *                available.
      */
     @Override
-    public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated,
-            final ArrayList<ShortcutInfo> removed, final UserHandle user) {
+    public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
         Runnable r = new Runnable() {
             public void run() {
-                bindShortcutsChanged(updated, removed, user);
+                bindShortcutsChanged(updated, user);
             }
         };
         if (waitUntilResume(r)) {
@@ -3811,31 +3761,6 @@
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
         }
-
-        if (!removed.isEmpty()) {
-            HashSet<ComponentName> removedComponents = new HashSet<>();
-            HashSet<ShortcutKey> removedDeepShortcuts = new HashSet<>();
-
-            for (ShortcutInfo si : removed) {
-                if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si));
-                } else {
-                    removedComponents.add(si.getTargetComponent());
-                }
-            }
-
-            if (!removedComponents.isEmpty()) {
-                ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user);
-                mWorkspace.removeItemsByMatcher(matcher);
-                mDragController.onAppsRemoved(matcher);
-            }
-
-            if (!removedDeepShortcuts.isEmpty()) {
-                ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts);
-                mWorkspace.removeItemsByMatcher(matcher);
-                mDragController.onAppsRemoved(matcher);
-            }
-        }
     }
 
     /**
@@ -3865,28 +3790,17 @@
      * package-removal should clear all items by package name.
      */
     @Override
-    public void bindWorkspaceComponentsRemoved(
-            final HashSet<String> packageNames, final HashSet<ComponentName> components,
-            final UserHandle user) {
+    public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
         Runnable r = new Runnable() {
             public void run() {
-                bindWorkspaceComponentsRemoved(packageNames, components, user);
+                bindWorkspaceComponentsRemoved(matcher);
             }
         };
         if (waitUntilResume(r)) {
             return;
         }
-        if (!packageNames.isEmpty()) {
-            ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user);
-            mWorkspace.removeItemsByMatcher(matcher);
-            mDragController.onAppsRemoved(matcher);
-
-        }
-        if (!components.isEmpty()) {
-            ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user);
-            mWorkspace.removeItemsByMatcher(matcher);
-            mDragController.onAppsRemoved(matcher);
-        }
+        mWorkspace.removeItemsByMatcher(matcher);
+        mDragController.onAppsRemoved(matcher);
     }
 
     @Override
@@ -3907,22 +3821,25 @@
         }
     }
 
-    private Runnable mBindAllWidgetsRunnable = new Runnable() {
+    @Override
+    public void bindAllWidgets(final MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
+        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
+            @Override
             public void run() {
-                bindAllWidgets(mAllWidgets);
+                bindAllWidgets(allWidgets);
             }
         };
-
-    @Override
-    public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
-        if (waitUntilResume(mBindAllWidgetsRunnable, true)) {
-            mAllWidgets = allWidgets;
+        if (waitUntilResume(r)) {
             return;
         }
 
         if (mWidgetsView != null && allWidgets != null) {
+            Executor pendingExecutor = getPendingExecutor();
+            if (pendingExecutor != null && mState != State.WIDGETS) {
+                pendingExecutor.execute(r);
+                return;
+            }
             mWidgetsView.setWidgets(allWidgets);
-            mAllWidgets = null;
         }
 
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -3947,7 +3864,7 @@
      *                    refreshes the widgets and shortcuts associated with the given package/user
      */
     public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
-        mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
+        mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
     }
 
     public void lockScreenOrientation() {
@@ -3978,19 +3895,8 @@
     }
 
     private boolean shouldShowDiscoveryBounce() {
-        if (mState != mState.WORKSPACE) {
-            return false;
-        }
-        if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
-            return true;
-        }
-        if (!mIsResumeFromActionScreenOff) {
-            return false;
-        }
-        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
-            return false;
-        }
-        return true;
+        UserManagerCompat um = UserManagerCompat.getInstance(this);
+        return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser();
     }
 
     protected void moveWorkspaceToDefaultScreen() {
@@ -4079,7 +3985,7 @@
             switch (keyCode) {
                 case KeyEvent.KEYCODE_A:
                     if (mState == State.WORKSPACE) {
-                        showAppsView(true, true, false);
+                        showAppsView(true, true);
                         return true;
                     }
                     break;
@@ -4126,9 +4032,8 @@
         public void onSharedPreferenceChanged(
                 SharedPreferences sharedPreferences, String key) {
             if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
-                // Finish this instance of the activity. When the activity is recreated,
-                // it will initialize the rotation preference again.
-                finish();
+                // Recreate the activity so that it initializes the rotation preference again.
+                recreate();
             }
         }
     }
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index aa7f5ee..cfb9b57 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,11 +21,10 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.graphics.drawable.Drawable;
 import android.util.Property;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.widget.ViewAnimator;
 
 import java.util.HashSet;
 import java.util.WeakHashMap;
@@ -130,4 +129,16 @@
         return anim;
     }
 
+    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+            new Property<Drawable, Integer>(Integer.TYPE, "drawableAlpha") {
+                @Override
+                public Integer get(Drawable drawable) {
+                    return drawable.getAlpha();
+                }
+
+                @Override
+                public void set(Drawable drawable, Integer alpha) {
+                    drawable.setAlpha(alpha);
+                }
+            };
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 180c202..1ffe41b 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
@@ -26,18 +27,22 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.SettingsObserver;
 import com.android.launcher3.util.TestingUtils;
 
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
 public class LauncherAppState {
 
-    public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+    public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     private static LauncherAppState INSTANCE;
@@ -47,7 +52,7 @@
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
-
+    private final SettingsObserver mNotificationBadgingObserver;
 
     public static LauncherAppState getInstance(final Context context) {
         if (INSTANCE == null) {
@@ -93,9 +98,7 @@
         mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-
-        mModel = new LauncherModel(this, mIconCache,
-                Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
+        mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
 
         LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
 
@@ -119,6 +122,23 @@
         new ConfigMonitor(mContext).register();
 
         ExtractionUtils.startColorExtractionServiceIfNecessary(mContext);
+
+        if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
+            mNotificationBadgingObserver = 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);
+        }
     }
 
     /**
@@ -129,6 +149,9 @@
         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
         launcherApps.removeOnAppsChangedCallback(mModel);
         PackageInstallerCompat.getInstance(mContext).onStop();
+        if (mNotificationBadgingObserver != null) {
+            mNotificationBadgingObserver.unregister();
+        }
     }
 
     LauncherModel setLauncher(Launcher launcher) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 6e8c59b..5573c5c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -16,12 +16,20 @@
 
 package com.android.launcher3;
 
+import android.app.Activity;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
+import android.widget.Toast;
+
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
 
@@ -33,14 +41,16 @@
  */
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
-    private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+    public static final int APPWIDGET_HOST_ID = 1024;
+
+    private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
     private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
 
-    private Launcher mLauncher;
+    private final Context mContext;
 
-    public LauncherAppWidgetHost(Launcher launcher, int hostId) {
-        super(launcher, hostId);
-        mLauncher = launcher;
+    public LauncherAppWidgetHost(Context context) {
+        super(context, APPWIDGET_HOST_ID);
+        mContext = context;
     }
 
     @Override
@@ -53,6 +63,10 @@
 
     @Override
     public void startListening() {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return;
+        }
+
         try {
             super.startListening();
         } catch (Exception e) {
@@ -66,24 +80,38 @@
         }
     }
 
-    public void addProviderChangeListener(Runnable callback) {
+    @Override
+    public void stopListening() {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return;
+        }
+
+        super.stopListening();
+    }
+
+    @Override
+    public int allocateAppWidgetId() {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return AppWidgetManager.INVALID_APPWIDGET_ID;
+        }
+
+        return super.allocateAppWidgetId();
+    }
+
+    public void addProviderChangeListener(ProviderChangedListener callback) {
         mProviderChangeListeners.add(callback);
     }
 
-    public void removeProviderChangeListener(Runnable callback) {
+    public void removeProviderChangeListener(ProviderChangedListener callback) {
         mProviderChangeListeners.remove(callback);
     }
 
     protected void onProvidersChanged() {
         if (!mProviderChangeListeners.isEmpty()) {
-            for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) {
-                callback.run();
+            for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) {
+                callback.notifyWidgetProvidersChanged();
             }
         }
-
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            mLauncher.notifyWidgetProvidersChanged();
-        }
     }
 
     public AppWidgetHostView createView(Context context, int appWidgetId,
@@ -109,7 +137,7 @@
                 // will update.
                 LauncherAppWidgetHostView view = mViews.get(appWidgetId);
                 if (view == null) {
-                    view = onCreateView(mLauncher, appWidgetId, appWidget);
+                    view = onCreateView(mContext, appWidgetId, appWidget);
                 }
                 view.setAppWidget(appWidgetId, appWidget);
                 view.switchToErrorView();
@@ -124,11 +152,11 @@
     @Override
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
         LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
-                mLauncher, appWidget);
+                mContext, appWidget);
         super.onProviderChanged(appWidgetId, info);
         // The super method updates the dimensions of the providerInfo. Update the
         // launcher spans accordingly.
-        info.initSpans(mLauncher);
+        info.initSpans(mContext);
     }
 
     @Override
@@ -142,4 +170,53 @@
         super.clearViews();
         mViews.clear();
     }
+
+    public void startBindFlow(BaseActivity activity,
+            int appWidgetId, AppWidgetProviderInfo info, int requestCode) {
+
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            sendActionCancelled(activity, requestCode);
+            return;
+        }
+
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
+                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
+                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider)
+                .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile());
+        // TODO: we need to make sure that this accounts for the options bundle.
+        // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
+        activity.startActivityForResult(intent, requestCode);
+    }
+
+
+    public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            sendActionCancelled(activity, requestCode);
+            return;
+        }
+
+        try {
+            startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+        } catch (ActivityNotFoundException | SecurityException e) {
+            Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            sendActionCancelled(activity, requestCode);
+        }
+    }
+
+    private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
+        new Handler().post(new Runnable() {
+            @Override
+            public void run() {
+                activity.onActivityResult(requestCode, Activity.RESULT_CANCELED, null);
+            }
+        });
+    }
+
+    /**
+     * Listener for getting notifications on provider changes.
+     */
+    public interface ProviderChangedListener {
+
+        void notifyWidgetProvidersChanged();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 13cc7ba..7fe1308 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -23,7 +23,6 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -40,9 +39,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
 
-import java.lang.reflect.Method;
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
 
 /**
  * {@inheritDoc}
@@ -50,8 +47,6 @@
 public class LauncherAppWidgetHostView extends AppWidgetHostView
         implements TouchCompleteListener, View.OnLongClickListener {
 
-    private static final String TAG = "LauncherWidgetHostView";
-
     // Related to the auto-advancing of widgets
     private static final long ADVANCE_INTERVAL = 20000;
     private static final long ADVANCE_STAGGER = 250;
@@ -97,14 +92,8 @@
         setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
 
-        if (Utilities.isAtLeastO()) {
-            try {
-                Method asyncMethod = AppWidgetHostView.class
-                        .getMethod("setExecutor", Executor.class);
-                asyncMethod.invoke(this, Utilities.THREAD_POOL_EXECUTOR);
-            } catch (Exception e) {
-                Log.e(TAG, "Unable to set async executor", e);
-            }
+        if (Utilities.ATLEAST_OREO) {
+            setExecutor(Utilities.THREAD_POOL_EXECUTOR);
         }
     }
 
@@ -153,9 +142,8 @@
         return false;
     }
 
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re-inflate is required if the orientation has changed since last inflated.
-        int orientation = mContext.getResources().getConfiguration().orientation;
         if (mPreviousOrientation != orientation) {
            return true;
        }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 1e0f285..6f23e56 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.Process;
 
+import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ContentWriter;
 
 /**
@@ -95,6 +96,11 @@
      */
     public Intent bindOptions;
 
+    /**
+     * Nonnull for pending widgets. We use this to get the icon and title for the widget.
+     */
+    public PackageItemInfo pendingItemInfo;
+
     private boolean mHasNotifiedInitialWidgetSizeChanged;
 
     public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2bac11f..66da046 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -17,14 +17,11 @@
 package com.android.launcher3;
 
 import android.content.Intent;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
 
-import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -44,69 +41,58 @@
      * Activity life-cycle methods. These methods are triggered after
      * the code in the corresponding Launcher method is executed.
      */
-    public void preOnCreate();
-    public void onCreate(Bundle savedInstanceState);
-    public void preOnResume();
-    public void onResume();
-    public void onStart();
-    public void onStop();
-    public void onPause();
-    public void onDestroy();
-    public void onSaveInstanceState(Bundle outState);
-    public void onPostCreate(Bundle savedInstanceState);
-    public void onNewIntent(Intent intent);
-    public void onActivityResult(int requestCode, int resultCode, Intent data);
-    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+    void preOnCreate();
+    void onCreate(Bundle savedInstanceState);
+    void preOnResume();
+    void onResume();
+    void onStart();
+    void onStop();
+    void onPause();
+    void onDestroy();
+    void onSaveInstanceState(Bundle outState);
+    void onPostCreate(Bundle savedInstanceState);
+    void onNewIntent(Intent intent);
+    void onActivityResult(int requestCode, int resultCode, Intent data);
+    void onRequestPermissionsResult(int requestCode, String[] permissions,
             int[] grantResults);
-    public void onWindowFocusChanged(boolean hasFocus);
-    public void onAttachedToWindow();
-    public void onDetachedFromWindow();
-    public boolean onPrepareOptionsMenu(Menu menu);
-    public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
-    public void onHomeIntent();
-    public boolean handleBackPressed();
-    public void onTrimMemory(int level);
+    void onWindowFocusChanged(boolean hasFocus);
+    void onAttachedToWindow();
+    void onDetachedFromWindow();
+    boolean onPrepareOptionsMenu(Menu menu);
+    void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
+    void onHomeIntent();
+    boolean handleBackPressed();
+    void onTrimMemory(int level);
 
     /*
      * Extension points for providing custom behavior on certain user interactions.
      */
-    public void onLauncherProviderChange();
-    public void finishBindingItems(final boolean upgradePath);
-    public void bindAllApplications(ArrayList<AppInfo> apps);
-    public void onInteractionBegin();
-    public void onInteractionEnd();
+    void onLauncherProviderChange();
+    void finishBindingItems(final boolean upgradePath);
+    void bindAllApplications(ArrayList<AppInfo> apps);
+    void onInteractionBegin();
+    void onInteractionEnd();
 
     @Deprecated
-    public void onWorkspaceLockedChanged();
+    void onWorkspaceLockedChanged();
 
     /**
      * Starts a search with {@param initialQuery}. Return false if search was not started.
      */
-    public boolean startSearch(
+    boolean startSearch(
             String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
-    public boolean hasCustomContentToLeft();
-    public void populateCustomContentContainer();
-    public View getQsbBar();
-    public Bundle getAdditionalSearchWidgetOptions();
+    boolean hasCustomContentToLeft();
+    void populateCustomContentContainer();
+    View getQsbBar();
+    Bundle getAdditionalSearchWidgetOptions();
 
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
-    public boolean shouldMoveToDefaultScreenOnHomeIntent();
-    public boolean hasSettings();
-    public AllAppsSearchBarController getAllAppsSearchBarController();
-    public List<ComponentKey> getPredictedApps();
-    public static final int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
+    boolean shouldMoveToDefaultScreenOnHomeIntent();
+    boolean hasSettings();
+    List<ComponentKeyMapper<AppInfo>> getPredictedApps();
+    int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
     /** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
-    public int getSearchBarHeight();
-
-    /**
-     * Sets the callbacks to allow reacting the actions of search overlays of the launcher.
-     *
-     * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback,
-     *                  but for implementation purposes is passed around as an object.
-     */
-    public void setLauncherSearchCallback(Object callbacks);
-
-    public boolean shouldShowDiscoveryBounce();
+    int getSearchBarHeight();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b2f6284..a906b00 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProviderOperation;
@@ -24,57 +23,41 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherActivityInfo;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
-import android.os.SystemClock;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.MutableInt;
+import android.util.Pair;
 
-import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.CacheDataUpdatedTask;
-import com.android.launcher3.model.ExtendedModelTask;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.PackageUpdatedTask;
-import com.android.launcher3.model.SdCardAvailableReceiver;
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.provider.ImportDataTask;
 import com.android.launcher3.provider.LauncherDbUtils;
 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.ManagedProfileHeuristic;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Provider;
@@ -85,14 +68,9 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 
@@ -103,21 +81,16 @@
  */
 public class LauncherModel extends BroadcastReceiver
         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
-    static final boolean DEBUG_LOADERS = false;
     private static final boolean DEBUG_RECEIVER = false;
 
     static final String TAG = "Launcher.Model";
 
-    private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
-    private static final long INVALID_SCREEN_ID = -1L;
-
+    private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
     @Thunk final LauncherAppState mApp;
     @Thunk final Object mLock = new Object();
-    @Thunk DeferredHandler mHandler = new DeferredHandler();
-    @Thunk LoaderTask mLoaderTask;
+    @Thunk
+    LoaderTask mLoaderTask;
     @Thunk boolean mIsLoaderTaskRunning;
-    @Thunk boolean mHasLoaderCompletedOnce;
-    @Thunk boolean mIsManagedHeuristicAppsUpdated;
 
     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
     static {
@@ -136,33 +109,10 @@
         }
     }
 
-    /**
-     * Set of runnables to be called on the background thread after the workspace binding
-     * is complete.
-     */
-    static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>();
-
     @Thunk WeakReference<Callbacks> mCallbacks;
 
     // < only access in worker thread >
     private final AllAppsList mBgAllAppsList;
-    // Entire list of widgets.
-    private final WidgetsModel mBgWidgetsModel;
-
-    private boolean mHasShortcutHostPermission;
-    // Runnable to check if the shortcuts permission has changed.
-    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mModelLoaded) {
-                boolean hasShortcutHostPermission =
-                        DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
-                if (hasShortcutHostPermission != mHasShortcutHostPermission) {
-                    forceReload();
-                }
-            }
-        }
-    };
 
     /**
      * All the static data should be accessed on the background thread, A lock should be acquired
@@ -170,39 +120,40 @@
      */
     static final BgDataModel sBgDataModel = new BgDataModel();
 
-    // </ only access in worker thread >
+    // Runnable to check if the shortcuts permission has changed.
+    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mModelLoaded) {
+                boolean hasShortcutHostPermission =
+                        DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
+                if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) {
+                    forceReload();
+                }
+            }
+        }
+    };
 
-    private final IconCache mIconCache;
-
-    private final LauncherAppsCompat mLauncherApps;
-    private final UserManagerCompat mUserManager;
-
-    public interface Callbacks {
+    public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener {
         public boolean setLoadOnResume();
         public int getCurrentWorkspaceScreen();
         public void clearPendingBinds();
         public void startBinding();
-        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
-                              boolean forceAnimateIcons);
+        public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
         public void bindScreens(ArrayList<Long> orderedScreenIds);
         public void finishFirstPageBind(ViewOnDrawExecutor executor);
         public void finishBindingItems();
-        public void bindAppWidget(LauncherAppWidgetInfo info);
         public void bindAllApplications(ArrayList<AppInfo> apps);
+        public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps);
         public void bindAppsAdded(ArrayList<Long> newScreens,
                                   ArrayList<ItemInfo> addNotAnimated,
-                                  ArrayList<ItemInfo> addAnimated,
-                                  ArrayList<AppInfo> addedApps);
-        public void bindAppsUpdated(ArrayList<AppInfo> apps);
-        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
-                ArrayList<ShortcutInfo> removed, UserHandle user);
+                                  ArrayList<ItemInfo> addAnimated);
+        public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, UserHandle user);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
-        public void bindWorkspaceComponentsRemoved(
-                HashSet<String> packageNames, HashSet<ComponentName> components,
-                UserHandle user);
+        public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
-        public void notifyWidgetProvidersChanged();
         public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
         public void onPageBoundSynchronously(int page);
         public void executeOnNextDraw(ViewOnDrawExecutor executor);
@@ -210,25 +161,8 @@
     }
 
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
-        Context context = app.getContext();
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
-        mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
-        mIconCache = iconCache;
-
-        mLauncherApps = LauncherAppsCompat.getInstance(context);
-        mUserManager = UserManagerCompat.getInstance(context);
-    }
-
-    /** Runs the specified runnable immediately if called from the main thread, otherwise it is
-     * posted on the main thread handler. */
-    private void runOnMainThread(Runnable r) {
-        if (sWorkerThread.getThreadId() == Process.myTid()) {
-            // If we are on the worker thread, post onto the main handler
-            mHandler.post(r);
-        } else {
-            r.run();
-        }
     }
 
     /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
@@ -259,15 +193,8 @@
     /**
      * Adds the provided items to the workspace.
      */
-    public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) {
-        addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
-    }
-
-    /**
-     * Adds the provided items to the workspace.
-     */
     public void addAndBindAddedWorkspaceItems(
-            Provider<List<ItemInfo>> appsProvider) {
+            Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
         enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
     }
 
@@ -380,8 +307,6 @@
     public void initialize(Callbacks callbacks) {
         synchronized (mLock) {
             Preconditions.assertUIThread();
-            // Remove any queued UI runnables
-            mHandler.cancelAll();
             mCallbacks = new WeakReference<>(callbacks);
         }
     }
@@ -493,7 +418,7 @@
     public void forceReload() {
         synchronized (mLock) {
             // Stop any existing loaders first, so they don't set mModelLoaded to true later
-            stopLoaderLocked();
+            stopLoader();
             mModelLoaded = false;
         }
 
@@ -519,16 +444,6 @@
         }
     }
 
-    /**
-     * If there is already a loader task running, tell it to stop.
-     */
-    private void stopLoaderLocked() {
-        LoaderTask oldTask = mLoaderTask;
-        if (oldTask != null) {
-            oldTask.stopLocked();
-        }
-    }
-
     public boolean isCurrentCallbacks(Callbacks callbacks) {
         return (mCallbacks != null && mCallbacks.get() == callbacks);
     }
@@ -539,42 +454,61 @@
      */
     public boolean startLoader(int synchronousBindPage) {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
-        InstallShortcutReceiver.enableInstallQueue();
+        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
         synchronized (mLock) {
             // Don't bother to start the thread if we know it's not going to do anything
             if (mCallbacks != null && mCallbacks.get() != null) {
                 final Callbacks oldCallbacks = mCallbacks.get();
                 // Clear any pending bind-runnables from the synchronized load process.
-                runOnMainThread(new Runnable() {
-                    public void run() {
-                        oldCallbacks.clearPendingBinds();
-                    }
-                });
+                mUiExecutor.execute(new Runnable() {
+                            public void run() {
+                                oldCallbacks.clearPendingBinds();
+                            }
+                        });
 
                 // If there is already one running, tell it to stop.
-                stopLoaderLocked();
-                mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
-                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
-                        && mModelLoaded && !mIsLoaderTaskRunning) {
-                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
+                stopLoader();
+                LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
+                        mBgAllAppsList, synchronousBindPage, mCallbacks);
+                if (mModelLoaded && !mIsLoaderTaskRunning) {
+                    // Divide the set of loaded items into those that we are binding synchronously,
+                    // and everything else that is to be bound normally (asynchronously).
+                    loaderResults.bindWorkspace();
+                    // For now, continue posting the binding of AllApps as there are other
+                    // issues that arise from that.
+                    loaderResults.bindAllApps();
+                    loaderResults.bindDeepShortcuts();
+                    loaderResults.bindWidgets();
                     return true;
                 } else {
-                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
-                    sWorker.post(mLoaderTask);
+                    startLoaderForResults(loaderResults);
                 }
             }
         }
         return false;
     }
 
+    /**
+     * If there is already a loader task running, tell it to stop.
+     */
     public void stopLoader() {
         synchronized (mLock) {
-            if (mLoaderTask != null) {
-                mLoaderTask.stopLocked();
+            LoaderTask oldTask = mLoaderTask;
+            mLoaderTask = null;
+            if (oldTask != null) {
+                oldTask.stopLocked();
             }
         }
     }
 
+    public void startLoaderForResults(LoaderResults results) {
+        synchronized (mLock) {
+            stopLoader();
+            mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+            runOnWorkerThread(mLoaderTask);
+        }
+    }
+
     /**
      * Loads the workspace screen ids in an ordered list.
      */
@@ -587,1230 +521,61 @@
                 screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
     }
 
-    /**
-     * Runnable for the thread that loads the contents of the launcher:
-     *   - workspace icons
-     *   - widgets
-     *   - all apps icons
-     *   - deep shortcuts within apps
-     */
-    private class LoaderTask implements Runnable {
-        private Context mContext;
-        private int mPageToBindFirst;
-
-        @Thunk boolean mIsLoadingAndBindingWorkspace;
-        private boolean mStopped;
-        @Thunk boolean mLoadAndBindStepFinished;
-
-        LoaderTask(Context context, int pageToBindFirst) {
-            mContext = context;
-            mPageToBindFirst = pageToBindFirst;
-        }
-
-        private void waitForIdle() {
-            // Wait until the either we're stopped or the other threads are done.
-            // This way we don't start loading all apps until the workspace has settled
-            // down.
-            synchronized (LoaderTask.this) {
-                final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
-                mHandler.postIdle(new Runnable() {
-                        public void run() {
-                            synchronized (LoaderTask.this) {
-                                mLoadAndBindStepFinished = true;
-                                if (DEBUG_LOADERS) {
-                                    Log.d(TAG, "done with previous binding step");
-                                }
-                                LoaderTask.this.notify();
-                            }
+    public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+        enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                apps.addPromiseApp(app.getContext(), sessionInfo);
+                if (!apps.added.isEmpty()) {
+                    final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
+                    apps.added.clear();
+                    scheduleCallbackTask(new CallbackTask() {
+                        @Override
+                        public void execute(Callbacks callbacks) {
+                            callbacks.bindAppsAddedOrUpdated(arrayList);
                         }
                     });
-
-                while (!mStopped && !mLoadAndBindStepFinished) {
-                    try {
-                        // Just in case mFlushingWorkerThread changes but we aren't woken up,
-                        // wait no longer than 1sec at a time
-                        this.wait(1000);
-                    } catch (InterruptedException ex) {
-                        // Ignore
-                    }
-                }
-                if (DEBUG_LOADERS) {
-                    Log.d(TAG, "waited "
-                            + (SystemClock.uptimeMillis()-workspaceWaitTime)
-                            + "ms for previous step to finish binding");
                 }
             }
-        }
+        });
+    }
 
-        void runBindSynchronousPage(int synchronousBindPage) {
-            if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
-                // Ensure that we have a valid page index to load synchronously
-                throw new RuntimeException("Should not call runBindSynchronousPage() without " +
-                        "valid page index");
-            }
-            if (!mModelLoaded) {
-                // Ensure that we don't try and bind a specified page when the pages have not been
-                // loaded already (we should load everything asynchronously in that case)
-                throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
-            }
+    public class LoaderTransaction implements AutoCloseable {
+
+        private final LoaderTask mTask;
+
+        private LoaderTransaction(LoaderTask task) throws CancellationException {
             synchronized (mLock) {
-                if (mIsLoaderTaskRunning) {
-                    // Ensure that we are never running the background loading at this point since
-                    // we also touch the background collections
-                    throw new RuntimeException("Error! Background loading is already running");
+                if (mLoaderTask != task) {
+                    throw new CancellationException("Loader already stopped");
                 }
-            }
-
-            // XXX: Throw an exception if we are already loading (since we touch the worker thread
-            //      data structures, we can't allow any other thread to touch that data, but because
-            //      this call is synchronous, we can get away with not locking).
-
-            // The LauncherModel is static in the LauncherAppState and mHandler may have queued
-            // operations from the previous activity.  We need to ensure that all queued operations
-            // are executed before any synchronous binding work is done.
-            mHandler.flush();
-
-            // Divide the set of loaded items into those that we are binding synchronously, and
-            // everything else that is to be bound normally (asynchronously).
-            bindWorkspace(synchronousBindPage);
-            // XXX: For now, continue posting the binding of AllApps as there are other issues that
-            //      arise from that.
-            onlyBindAllApps();
-
-            bindDeepShortcuts();
-        }
-
-        private void verifyNotStopped() throws CancellationException {
-            synchronized (LoaderTask.this) {
-                if (mStopped) {
-                    throw new CancellationException("Loader stopped");
-                }
-            }
-        }
-
-        public void run() {
-            synchronized (mLock) {
-                if (mStopped) {
-                    return;
-                }
+                mTask = task;
                 mIsLoaderTaskRunning = true;
-            }
-
-            try {
-                if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
-                // Set to false in bindWorkspace()
-                mIsLoadingAndBindingWorkspace = true;
-                loadWorkspace();
-
-                verifyNotStopped();
-                if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
-                bindWorkspace(mPageToBindFirst);
-
-                // Take a break
-                if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
-                waitForIdle();
-                verifyNotStopped();
-
-                // second step
-                if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
-                loadAllApps();
-
-                verifyNotStopped();
-                if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
-                updateIconCache();
-
-                // Take a break
-                if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
-                waitForIdle();
-                verifyNotStopped();
-
-                // third step
-                if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
-                loadDeepShortcuts();
-
-                verifyNotStopped();
-                if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
-                bindDeepShortcuts();
-
-                // Take a break
-                if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
-                waitForIdle();
-                verifyNotStopped();
-
-                // fourth step
-                if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
-                refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
-                        null /* packageUser */);
-
-                synchronized (mLock) {
-                    // Everything loaded bind the data.
-                    mModelLoaded = true;
-                    mHasLoaderCompletedOnce = true;
-                }
-            } catch (CancellationException e) {
-              // Loader stopped, ignore
-            } finally {
-                // Clear out this reference, otherwise we end up holding it until all of the
-                // callback runnables are done.
-                mContext = null;
-
-                synchronized (mLock) {
-                    // If we are still the last one to be scheduled, remove ourselves.
-                    if (mLoaderTask == this) {
-                        mLoaderTask = null;
-                    }
-                    mIsLoaderTaskRunning = false;
-                }
+                mModelLoaded = false;
             }
         }
 
-        public void stopLocked() {
-            synchronized (LoaderTask.this) {
-                mStopped = true;
-                this.notify();
-            }
-        }
-
-        /**
-         * Gets the callbacks object.  If we've been stopped, or if the launcher object
-         * has somehow been garbage collected, return null instead.  Pass in the Callbacks
-         * object that was around when the deferred message was scheduled, and if there's
-         * a new Callbacks object around then also return null.  This will save us from
-         * calling onto it with data that will be ignored.
-         */
-        Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
+        public void commit() {
             synchronized (mLock) {
-                if (mStopped) {
-                    return null;
-                }
-
-                if (mCallbacks == null) {
-                    return null;
-                }
-
-                final Callbacks callbacks = mCallbacks.get();
-                if (callbacks != oldCallbacks) {
-                    return null;
-                }
-                if (callbacks == null) {
-                    Log.w(TAG, "no mCallbacks");
-                    return null;
-                }
-
-                return callbacks;
+                // Everything loaded bind the data.
+                mModelLoaded = true;
             }
         }
 
-        private void loadWorkspace() {
-            if (LauncherAppState.PROFILE_STARTUP) {
-                Trace.beginSection("Loading Workspace");
-            }
-
-            final Context context = mContext;
-            final ContentResolver contentResolver = context.getContentResolver();
-            final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
-            final boolean isSafeMode = pmHelper.isSafeMode();
-            final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-            final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
-            final boolean isSdCardReady = Utilities.isBootCompleted();
-            final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
-
-            boolean clearDb = false;
-            try {
-                ImportDataTask.performImportIfPossible(context);
-            } catch (Exception e) {
-                // Migration failed. Clear workspace.
-                clearDb = true;
-            }
-
-            if (!clearDb && GridSizeMigrationTask.ENABLED &&
-                    !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
-                // Migration failed. Clear workspace.
-                clearDb = true;
-            }
-
-            if (clearDb) {
-                Log.d(TAG, "loadWorkspace: resetting launcher database");
-                LauncherSettings.Settings.call(contentResolver,
-                        LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-            }
-
-            Log.d(TAG, "loadWorkspace: loading default favorites");
-            LauncherSettings.Settings.call(contentResolver,
-                    LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
-
-            synchronized (sBgDataModel) {
-                sBgDataModel.clear();
-
-                final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
-                        .getInstance(mContext).updateAndGetActiveSessionCache();
-                sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
-                Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
-                final LoaderCursor c = new LoaderCursor(contentResolver.query(
-                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
-
-                HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
-
-                try {
-                    final int appWidgetIdIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.APPWIDGET_ID);
-                    final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.APPWIDGET_PROVIDER);
-                    final int spanXIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.SPANX);
-                    final int spanYIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.SPANY);
-                    final int rankIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.RANK);
-                    final int optionsIndex = c.getColumnIndexOrThrow(
-                            LauncherSettings.Favorites.OPTIONS);
-
-                    final LongSparseArray<UserHandle> allUsers = c.allUsers;
-                    final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
-                    final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
-                    for (UserHandle user : mUserManager.getUserProfiles()) {
-                        long serialNo = mUserManager.getSerialNumberForUser(user);
-                        allUsers.put(serialNo, user);
-                        quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
-
-                        boolean userUnlocked = mUserManager.isUserUnlocked(user);
-
-                        // We can only query for shortcuts when the user is unlocked.
-                        if (userUnlocked) {
-                            List<ShortcutInfoCompat> pinnedShortcuts =
-                                    shortcutManager.queryForPinnedShortcuts(null, user);
-                            if (shortcutManager.wasLastCallSuccess()) {
-                                for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
-                                    shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
-                                            shortcut);
-                                }
-                            } else {
-                                // Shortcut manager can fail due to some race condition when the
-                                // lock state changes too frequently. For the purpose of the loading
-                                // shortcuts, consider the user is still locked.
-                                userUnlocked = false;
-                            }
-                        }
-                        unlockedUsers.put(serialNo, userUnlocked);
-                    }
-
-                    ShortcutInfo info;
-                    LauncherAppWidgetInfo appWidgetInfo;
-                    Intent intent;
-                    String targetPkg;
-
-                    while (!mStopped && c.moveToNext()) {
-                        try {
-                            if (c.user == null) {
-                                // User has been deleted, remove the item.
-                                c.markDeleted("User has been deleted");
-                                continue;
-                            }
-
-                            boolean allowMissingTarget = false;
-                            switch (c.itemType) {
-                            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                                intent = c.parseIntent();
-                                if (intent == null) {
-                                    c.markDeleted("Invalid or null intent");
-                                    continue;
-                                }
-
-                                int disabledState = quietMode.get(c.serialNumber) ?
-                                        ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
-                                ComponentName cn = intent.getComponent();
-                                targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
-
-                                if (!Process.myUserHandle().equals(c.user)) {
-                                    if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                                        c.markDeleted("Legacy shortcuts are only allowed for default user");
-                                        continue;
-                                    } else if (c.restoreFlag != 0) {
-                                        // Don't restore items for other profiles.
-                                        c.markDeleted("Restore from managed profile not supported");
-                                        continue;
-                                    }
-                                }
-                                if (TextUtils.isEmpty(targetPkg) &&
-                                        c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                                    c.markDeleted("Only legacy shortcuts can have null package");
-                                    continue;
-                                }
-
-                                // If there is no target package, its an implicit intent
-                                // (legacy shortcut) which is always valid
-                                boolean validTarget = TextUtils.isEmpty(targetPkg) ||
-                                        launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
-
-                                if (cn != null && validTarget) {
-                                    // If the apk is present and the shortcut points to a specific
-                                    // component.
-
-                                    // If the component is already present
-                                    if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
-                                        // no special handling necessary for this item
-                                        c.markRestored();
-                                    } else {
-                                        if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_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;
-                                            }
-                                        } else {
-                                            // The app is installed but the component is no
-                                            // longer available.
-                                            c.markDeleted("Invalid component removed: " + cn);
-                                            continue;
-                                        }
-                                    }
-                                }
-                                // else if cn == null => can't infer much, leave it
-                                // else if !validPkg => could be restored icon or missing sd-card
-
-                                if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
-                                    // Points to a valid app (superset of cn != null) but the apk
-                                    // is not available.
-
-                                    if (c.restoreFlag != 0) {
-                                        // Package is not yet available but might be
-                                        // installed later.
-                                        FileLog.d(TAG, "package not yet restored: " + targetPkg);
-
-                                        if (c.hasRestoreFlag(ShortcutInfo.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();
-                                        } 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;
-                                        // Add the icon on the workspace anyway.
-                                        allowMissingTarget = true;
-                                    } else if (!isSdCardReady) {
-                                        // SdCard is not ready yet. Package might get available,
-                                        // once it is ready.
-                                        Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
-                                        pendingPackages.addToList(c.user, targetPkg);
-                                        // Add the icon on the workspace anyway.
-                                        allowMissingTarget = true;
-                                    } else {
-                                        // Do not wait for external media load anymore.
-                                        c.markDeleted("Invalid package removed: " + targetPkg);
-                                        continue;
-                                    }
-                                }
-
-                                if (validTarget) {
-                                    // The shortcut points to a valid target (either no target
-                                    // or something which is ready to be used)
-                                    c.markRestored();
-                                }
-
-                                boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
-                                        c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
-
-                                if (c.restoreFlag != 0) {
-                                    // Already verified above that user is same as default user
-                                    info = c.getRestoredItemInfo(intent);
-                                } else if (c.itemType ==
-                                        LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = c.getAppShortcutInfo(
-                                            intent, allowMissingTarget, useLowResIcon);
-                                } else if (c.itemType ==
-                                        LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-
-                                    ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
-                                    if (unlockedUsers.get(c.serialNumber)) {
-                                        ShortcutInfoCompat 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);
-                                        info.iconBitmap = LauncherIcons
-                                                .createShortcutIcon(pinnedShortcut, context);
-                                        if (pmHelper.isAppSuspended(
-                                                pinnedShortcut.getPackage(), info.user)) {
-                                            info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
-                                        }
-                                        intent = info.intent;
-                                    } else {
-                                        // Create a shortcut info in disabled mode for now.
-                                        info = c.loadSimpleShortcut();
-                                        info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
-                                    }
-                                } else { // item type == ITEM_TYPE_SHORTCUT
-                                    info = c.loadSimpleShortcut();
-
-                                    // Shortcuts are only available on the primary profile
-                                    if (!TextUtils.isEmpty(targetPkg)
-                                            && pmHelper.isAppSuspended(targetPkg, c.user)) {
-                                        disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
-                                    }
-
-                                    // App shortcuts that used to be automatically added to Launcher
-                                    // didn't always have the correct intent flags set, so do that
-                                    // here
-                                    if (intent.getAction() != null &&
-                                        intent.getCategories() != null &&
-                                        intent.getAction().equals(Intent.ACTION_MAIN) &&
-                                        intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
-                                        intent.addFlags(
-                                            Intent.FLAG_ACTIVITY_NEW_TASK |
-                                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                                    }
-                                }
-
-                                if (info != null) {
-                                    c.applyCommonProperties(info);
-
-                                    info.intent = intent;
-                                    info.rank = c.getInt(rankIndex);
-                                    info.spanX = 1;
-                                    info.spanY = 1;
-                                    info.isDisabled |= disabledState;
-                                    if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
-                                        info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
-                                    }
-
-                                    if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
-                                        Integer progress = installingPkgs.get(targetPkg);
-                                        if (progress != null) {
-                                            info.setInstallProgress(progress);
-                                        } else {
-                                            info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
-                                        }
-                                    }
-
-                                    c.checkAndAddItem(info, sBgDataModel);
-                                } else {
-                                    throw new RuntimeException("Unexpected null ShortcutInfo");
-                                }
-                                break;
-
-                            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                                FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
-                                c.applyCommonProperties(folderInfo);
-
-                                // Do not trim the folder label, as is was set by the user.
-                                folderInfo.title = c.getString(c.titleIndex);
-                                folderInfo.spanX = 1;
-                                folderInfo.spanY = 1;
-                                folderInfo.options = c.getInt(optionsIndex);
-
-                                // no special handling required for restored folders
-                                c.markRestored();
-
-                                c.checkAndAddItem(folderInfo, sBgDataModel);
-                                break;
-
-                            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                            case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                                // Read all Launcher-specific widget details
-                                boolean customWidget = c.itemType ==
-                                    LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-
-                                int appWidgetId = c.getInt(appWidgetIdIndex);
-                                String savedProvider = c.getString(appWidgetProviderIndex);
-
-                                final ComponentName component =
-                                        ComponentName.unflattenFromString(savedProvider);
-
-                                final boolean isIdValid = !c.hasRestoreFlag(
-                                        LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
-                                final boolean wasProviderReady = !c.hasRestoreFlag(
-                                        LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
-
-                                if (widgetProvidersMap == null) {
-                                    widgetProvidersMap = AppWidgetManagerCompat
-                                            .getInstance(mContext).getAllProvidersMap();
-                                }
-                                final AppWidgetProviderInfo provider = widgetProvidersMap.get(
-                                        new ComponentKey(
-                                                ComponentName.unflattenFromString(savedProvider),
-                                                c.user));
-
-                                final boolean isProviderReady = isValidProvider(provider);
-                                if (!isSafeMode && !customWidget &&
-                                        wasProviderReady && !isProviderReady) {
-                                    c.markDeleted(
-                                            "Deleting widget that isn't installed anymore: "
-                                            + provider);
-                                } else {
-                                    if (isProviderReady) {
-                                        appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
-                                                provider.provider);
-
-                                        // The provider is available. So the widget is either
-                                        // available or not available. We do not need to track
-                                        // any future restore updates.
-                                        int status = c.restoreFlag &
-                                                ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
-                                        if (!wasProviderReady) {
-                                            // If provider was not previously ready, update the
-                                            // status and UI flag.
-
-                                            // Id would be valid only if the widget restore broadcast was received.
-                                            if (isIdValid) {
-                                                status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-                                            } else {
-                                                status &= ~LauncherAppWidgetInfo
-                                                        .FLAG_PROVIDER_NOT_READY;
-                                            }
-                                        }
-                                        appWidgetInfo.restoreStatus = status;
-                                    } else {
-                                        Log.v(TAG, "Widget restore pending id=" + c.id
-                                                + " appWidgetId=" + appWidgetId
-                                                + " status =" + c.restoreFlag);
-                                        appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
-                                                component);
-                                        appWidgetInfo.restoreStatus = c.restoreFlag;
-                                        Integer installProgress = installingPkgs.get(component.getPackageName());
-
-                                        if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
-                                            // Restore has started once.
-                                        } else if (installProgress != null) {
-                                            // App restore has started. Update the flag
-                                            appWidgetInfo.restoreStatus |=
-                                                    LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
-                                        } else if (!isSafeMode) {
-                                            c.markDeleted("Unrestored widget removed: " + component);
-                                            continue;
-                                        }
-
-                                        appWidgetInfo.installProgress =
-                                                installProgress == null ? 0 : installProgress;
-                                    }
-                                    if (appWidgetInfo.hasRestoreFlag(
-                                            LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
-                                        appWidgetInfo.bindOptions = c.parseIntent();
-                                    }
-
-                                    c.applyCommonProperties(appWidgetInfo);
-                                    appWidgetInfo.spanX = c.getInt(spanXIndex);
-                                    appWidgetInfo.spanY = c.getInt(spanYIndex);
-                                    appWidgetInfo.user = c.user;
-
-                                    if (!c.isOnWorkspaceOrHotseat()) {
-                                        c.markDeleted("Widget found where container != " +
-                                                "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
-                                        continue;
-                                    }
-
-                                    if (!customWidget) {
-                                        String providerName =
-                                                appWidgetInfo.providerName.flattenToString();
-                                        if (!providerName.equals(savedProvider) ||
-                                                (appWidgetInfo.restoreStatus != c.restoreFlag)) {
-                                            c.updater()
-                                                    .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
-                                                            providerName)
-                                                    .put(LauncherSettings.Favorites.RESTORED,
-                                                            appWidgetInfo.restoreStatus)
-                                                    .commit();
-                                        }
-                                    }
-                                    c.checkAndAddItem(appWidgetInfo, sBgDataModel);
-                                }
-                                break;
-                            }
-                        } catch (Exception e) {
-                            Log.e(TAG, "Desktop items loading interrupted", e);
-                        }
-                    }
-                } finally {
-                    Utilities.closeSilently(c);
+        @Override
+        public void close() {
+            synchronized (mLock) {
+                // If we are still the last one to be scheduled, remove ourselves.
+                if (mLoaderTask == mTask) {
+                    mLoaderTask = null;
                 }
-
-                // Break early if we've stopped loading
-                if (mStopped) {
-                    sBgDataModel.clear();
-                    return;
-                }
-
-                // Remove dead items
-                if (c.commitDeleted()) {
-                    // Remove any empty folder
-                    ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
-                            .call(contentResolver,
-                                    LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
-                            .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
-                    for (long folderId : deletedFolderIds) {
-                        sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
-                        sBgDataModel.folders.remove(folderId);
-                        sBgDataModel.itemsIdMap.remove(folderId);
-                    }
-
-                    // Remove any ghost widgets
-                    LauncherSettings.Settings.call(contentResolver,
-                            LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
-                }
-
-                // Unpin shortcuts that don't exist on the workspace.
-                HashSet<ShortcutKey> pendingShortcuts =
-                        InstallShortcutReceiver.getPendingShortcuts(context);
-                for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
-                    MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
-                    if ((numTimesPinned == null || numTimesPinned.value == 0)
-                            && !pendingShortcuts.contains(key)) {
-                        // Shortcut is pinned but doesn't exist on the workspace; unpin it.
-                        shortcutManager.unpinShortcut(key);
-                    }
-                }
-
-                // Sort all the folder items and make sure the first 3 items are high resolution.
-                for (FolderInfo folder : sBgDataModel.folders) {
-                    Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
-                    int pos = 0;
-                    for (ShortcutInfo info : folder.contents) {
-                        if (info.usingLowResIcon &&
-                                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                            mIconCache.getTitleAndIcon(info, false);
-                        }
-                        pos ++;
-                        if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
-                            break;
-                        }
-                    }
-                }
-
-                c.commitRestoredItems();
-                if (!isSdCardReady && !pendingPackages.isEmpty()) {
-                    context.registerReceiver(
-                            new SdCardAvailableReceiver(
-                                    LauncherModel.this, mContext, pendingPackages),
-                            new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
-                            null,
-                            sWorker);
-                }
-
-                // Remove any empty screens
-                ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
-                for (ItemInfo item: sBgDataModel.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) {
-                    sBgDataModel.workspaceScreens.removeAll(unusedScreens);
-                    updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
-                }
-            }
-            if (LauncherAppState.PROFILE_STARTUP) {
-                Trace.endSection();
-            }
-        }
-
-        /** Filters the set of items who are directly or indirectly (via another container) on the
-         * specified screen. */
-        private void filterCurrentWorkspaceItems(long currentScreenId,
-                ArrayList<ItemInfo> allWorkspaceItems,
-                ArrayList<ItemInfo> currentScreenItems,
-                ArrayList<ItemInfo> otherScreenItems) {
-            // Purge any null ItemInfos
-            Iterator<ItemInfo> 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<Long>();
-            Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
-                @Override
-                public int compare(ItemInfo lhs, ItemInfo rhs) {
-                    return Utilities.longCompare(lhs.container, rhs.container);
-                }
-            });
-            for (ItemInfo 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);
-                    }
-                }
-            }
-        }
-
-        /** Filters the set of widgets which are on the specified screen. */
-        private void filterCurrentAppWidgets(long currentScreenId,
-                ArrayList<LauncherAppWidgetInfo> appWidgets,
-                ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
-                ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
-
-            for (LauncherAppWidgetInfo widget : appWidgets) {
-                if (widget == null) continue;
-                if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                        widget.screenId == currentScreenId) {
-                    currentScreenWidgets.add(widget);
-                } else {
-                    otherScreenWidgets.add(widget);
-                }
-            }
-        }
-
-        /** 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 (ProviderConfig.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 bindWorkspaceScreens(final Callbacks oldCallbacks,
-                final ArrayList<Long> orderedScreens) {
-            final Runnable r = new Runnable() {
-                @Override
-                public void run() {
-                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.bindScreens(orderedScreens);
-                    }
-                }
-            };
-            runOnMainThread(r);
-        }
-
-        private void bindWorkspaceItems(final Callbacks oldCallbacks,
-                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 = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null) {
-                            callbacks.bindItems(workspaceItems, 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 LauncherAppWidgetInfo widget = appWidgets.get(i);
-                final Runnable r = new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null) {
-                            callbacks.bindAppWidget(widget);
-                        }
-                    }
-                };
-                executor.execute(r);
-            }
-        }
-
-        /**
-         * Binds all loaded data to actual views on the main thread.
-         */
-        private void bindWorkspace(int synchronizeBindPage) {
-            final long t = SystemClock.uptimeMillis();
-            Runnable r;
-
-            // Don't use these two variables in any of the callback runnables.
-            // Otherwise we hold a reference to them.
-            final Callbacks oldCallbacks = mCallbacks.get();
-            if (oldCallbacks == 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<>();
-            ArrayList<Long> orderedScreenIds = new ArrayList<>();
-
-            synchronized (sBgDataModel) {
-                workspaceItems.addAll(sBgDataModel.workspaceItems);
-                appWidgets.addAll(sBgDataModel.appWidgets);
-                orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
-            }
-
-            final int currentScreen;
-            {
-                int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
-                        ? synchronizeBindPage : oldCallbacks.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);
-            filterCurrentAppWidgets(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 = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.clearPendingBinds();
-                        callbacks.startBinding();
-                    }
-                }
-            };
-            runOnMainThread(r);
-
-            bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
-
-            Executor mainExecutor = new DeferredMainThreadExecutor();
-            // Load items on the current page.
-            bindWorkspaceItems(oldCallbacks, 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(mHandler) : mainExecutor;
-
-            mainExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.finishFirstPageBind(
-                                validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
-                    }
-                }
-            });
-
-            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
-
-            // Tell the workspace that we're done binding items
-            r = new Runnable() {
-                public void run() {
-                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.finishBindingItems();
-                    }
-
-                    mIsLoadingAndBindingWorkspace = false;
-
-                    // Run all the bind complete runnables after workspace is bound.
-                    if (!mBindCompleteRunnables.isEmpty()) {
-                        synchronized (mBindCompleteRunnables) {
-                            for (final Runnable r : mBindCompleteRunnables) {
-                                runOnWorkerThread(r);
-                            }
-                            mBindCompleteRunnables.clear();
-                        }
-                    }
-
-                    // If we're profiling, ensure this is the last thing in the queue.
-                    if (DEBUG_LOADERS) {
-                        Log.d(TAG, "bound workspace in "
-                            + (SystemClock.uptimeMillis()-t) + "ms");
-                    }
-
-                }
-            };
-            deferredExecutor.execute(r);
-
-            if (validFirstPage) {
-                r = new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        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);
-                        }
-                    }
-                };
-                runOnMainThread(r);
-            }
-        }
-
-        private void updateIconCache() {
-            // Ignore packages which have a promise icon.
-            HashSet<String> packagesToIgnore = new HashSet<>();
-            synchronized (sBgDataModel) {
-                for (ItemInfo info : sBgDataModel.itemsIdMap) {
-                    if (info instanceof ShortcutInfo) {
-                        ShortcutInfo si = (ShortcutInfo) info;
-                        if (si.isPromise() && si.getTargetComponent() != null) {
-                            packagesToIgnore.add(si.getTargetComponent().getPackageName());
-                        }
-                    } else if (info instanceof LauncherAppWidgetInfo) {
-                        LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
-                        if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
-                            packagesToIgnore.add(lawi.providerName.getPackageName());
-                        }
-                    }
-                }
-            }
-            mIconCache.updateDbIcons(packagesToIgnore);
-        }
-
-        private void onlyBindAllApps() {
-            final Callbacks oldCallbacks = mCallbacks.get();
-            if (oldCallbacks == null) {
-                // This launcher has exited and nobody bothered to tell us.  Just bail.
-                Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
-                return;
-            }
-
-            // shallow copy
-            @SuppressWarnings("unchecked")
-            final ArrayList<AppInfo> list
-                    = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
-            Runnable r = new Runnable() {
-                public void run() {
-                    final long t = SystemClock.uptimeMillis();
-                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.bindAllApplications(list);
-                    }
-                    if (DEBUG_LOADERS) {
-                        Log.d(TAG, "bound all " + list.size() + " apps from cache in "
-                                + (SystemClock.uptimeMillis() - t) + "ms");
-                    }
-                }
-            };
-            runOnMainThread(r);
-        }
-
-        private void scheduleManagedHeuristicRunnable(final ManagedProfileHeuristic heuristic,
-                final UserHandle user, final List<LauncherActivityInfo> apps) {
-            if (heuristic != null) {
-                // Assume the app lists now is updated.
-                mIsManagedHeuristicAppsUpdated = false;
-                final Runnable managedHeuristicRunnable = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mIsManagedHeuristicAppsUpdated) {
-                            // If app list is updated, we need to reschedule it otherwise old app
-                            // list will override everything in processUserApps().
-                            sWorker.post(new Runnable() {
-                                public void run() {
-                                    final List<LauncherActivityInfo> updatedApps =
-                                            mLauncherApps.getActivityList(null, user);
-                                    scheduleManagedHeuristicRunnable(heuristic, user,
-                                            updatedApps);
-                                }
-                            });
-                        } else {
-                            heuristic.processUserApps(apps);
-                        }
-                    }
-                };
-                runOnMainThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        // Check isLoadingWorkspace on the UI thread, as it is updated on the UI
-                        // thread.
-                        if (mIsLoadingAndBindingWorkspace) {
-                            synchronized (mBindCompleteRunnables) {
-                                mBindCompleteRunnables.add(managedHeuristicRunnable);
-                            }
-                        } else {
-                            runOnWorkerThread(managedHeuristicRunnable);
-                        }
-                    }
-                });
-            }
-        }
-
-        private void loadAllApps() {
-            final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
-            final Callbacks oldCallbacks = mCallbacks.get();
-            if (oldCallbacks == null) {
-                // This launcher has exited and nobody bothered to tell us.  Just bail.
-                Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
-                return;
-            }
-
-            final List<UserHandle> profiles = mUserManager.getUserProfiles();
-
-            // Clear the list of apps
-            mBgAllAppsList.clear();
-            for (UserHandle user : profiles) {
-                // Query for the set of apps
-                final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-                final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
-                if (DEBUG_LOADERS) {
-                    Log.d(TAG, "getActivityList took "
-                            + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
-                    Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
-                }
-                // Fail if we don't have any apps
-                // TODO: Fix this. Only fail for the current user.
-                if (apps == null || apps.isEmpty()) {
-                    return;
-                }
-                boolean quietMode = mUserManager.isQuietModeEnabled(user);
-                // Create the ApplicationInfos
-                for (int i = 0; i < apps.size(); i++) {
-                    LauncherActivityInfo app = apps.get(i);
-                    // This builds the icon bitmaps.
-                    mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
-                }
-                final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
-                if (heuristic != null) {
-                    scheduleManagedHeuristicRunnable(heuristic, user, apps);
-                }
-            }
-            // Huh? Shouldn't this be inside the Runnable below?
-            final ArrayList<AppInfo> added = mBgAllAppsList.added;
-            mBgAllAppsList.added = new ArrayList<AppInfo>();
-
-            // Post callback on main thread
-            mHandler.post(new Runnable() {
-                public void run() {
-
-                    final long bindTime = SystemClock.uptimeMillis();
-                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                    if (callbacks != null) {
-                        callbacks.bindAllApplications(added);
-                        if (DEBUG_LOADERS) {
-                            Log.d(TAG, "bound " + added.size() + " apps in "
-                                    + (SystemClock.uptimeMillis() - bindTime) + "ms");
-                        }
-                    } else {
-                        Log.i(TAG, "not binding apps: no Launcher activity");
-                    }
-                }
-            });
-            // Cleanup any data stored for a deleted user.
-            ManagedProfileHeuristic.processAllUsers(profiles, mContext);
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "Icons processed in "
-                        + (SystemClock.uptimeMillis() - loadTime) + "ms");
-            }
-        }
-
-        private void loadDeepShortcuts() {
-            sBgDataModel.deepShortcutMap.clear();
-            DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
-            mHasShortcutHostPermission = shortcutManager.hasHostPermission();
-            if (mHasShortcutHostPermission) {
-                for (UserHandle user : mUserManager.getUserProfiles()) {
-                    if (mUserManager.isUserUnlocked(user)) {
-                        List<ShortcutInfoCompat> shortcuts =
-                                shortcutManager.queryForAllShortcuts(user);
-                        sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
-                    }
-                }
+                mIsLoaderTaskRunning = false;
             }
         }
     }
 
-    public void bindDeepShortcuts() {
-        final MultiHashMap<ComponentKey, String> shortcutMapCopy =
-                sBgDataModel.deepShortcutMap.clone();
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Callbacks callbacks = getCallback();
-                if (callbacks != null) {
-                    callbacks.bindDeepShortcutMap(shortcutMapCopy);
-                }
-            }
-        };
-        runOnMainThread(r);
+    public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+        return new LoaderTransaction(task);
     }
 
     /**
@@ -1835,14 +600,8 @@
                 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
     }
 
-    void enqueueModelUpdateTask(BaseModelUpdateTask task) {
-        if (!mModelLoaded && mLoaderTask == null) {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
-            }
-            return;
-        }
-        task.init(this);
+    public void enqueueModelUpdateTask(ModelUpdateTask task) {
+        task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
         runOnWorkerThread(task);
     }
 
@@ -1858,51 +617,14 @@
     /**
      * A runnable which changes/updates the data model of the launcher based on certain events.
      */
-    public static abstract class BaseModelUpdateTask implements Runnable {
-
-        private LauncherModel mModel;
-        private DeferredHandler mUiHandler;
-
-        /* package private */
-        void init(LauncherModel model) {
-            mModel = model;
-            mUiHandler = mModel.mHandler;
-        }
-
-        @Override
-        public void run() {
-            if (!mModel.mHasLoaderCompletedOnce) {
-                // Loader has not yet run.
-                return;
-            }
-            execute(mModel.mApp, sBgDataModel, mModel.mBgAllAppsList);
-        }
+    public interface ModelUpdateTask extends Runnable {
 
         /**
-         * Execute the actual task. Called on the worker thread.
+         * Called before the task is posted to initialize the internal state.
          */
-        public abstract void execute(
-                LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+        void init(LauncherAppState app, LauncherModel model,
+                BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
 
-        /**
-         * Schedules a {@param task} to be executed on the current callbacks.
-         */
-        public final void scheduleCallbackTask(final CallbackTask task) {
-            final Callbacks callbacks = mModel.getCallback();
-            mUiHandler.post(new Runnable() {
-                public void run() {
-                    Callbacks cb = mModel.getCallback();
-                    if (callbacks == cb && cb != null) {
-                        task.execute(callbacks);
-                    }
-                }
-            });
-        }
-
-        public ModelWriter getModelWriter() {
-            // Updates from model task, do not deal with icon position in hotseat.
-            return mModel.getWriter(false /* hasVerticalHotseat */);
-        }
     }
 
     public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
@@ -1920,7 +642,7 @@
      * Utility method to update a shortcut on the background thread.
      */
     public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
-        enqueueModelUpdateTask(new ExtendedModelTask() {
+        enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
                 ShortcutInfo info = shortcutProvider.get();
@@ -1931,43 +653,16 @@
         });
     }
 
-    private void bindWidgetsModel(final Callbacks callbacks) {
-        final MultiHashMap<PackageItemInfo, WidgetItem> widgets
-                = mBgWidgetsModel.getWidgetsMap().clone();
-        mHandler.post(new Runnable() {
+    public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
+        enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
-            public void run() {
-                Callbacks cb = getCallback();
-                if (callbacks == cb && cb != null) {
-                    callbacks.bindAllWidgets(widgets);
-                }
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                dataModel.widgetsModel.update(app, packageUser);
+                bindUpdatedWidgets(dataModel);
             }
         });
     }
 
-    public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
-            final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
-        runOnWorkerThread(new Runnable() {
-            @Override
-            public void run() {
-                if (bindFirst && !mBgWidgetsModel.isEmpty()) {
-                    bindWidgetsModel(callbacks);
-                }
-                ArrayList<WidgetItem> widgets = mBgWidgetsModel.update(
-                        mApp.getContext(), packageUser);
-                bindWidgetsModel(callbacks);
-
-                // update the Widget entries inside DB on the worker thread.
-                mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
-            }
-        });
-    }
-
-    static boolean isValidProvider(AppWidgetProviderInfo provider) {
-        return (provider != null) && (provider.provider != null)
-                && (provider.provider.getPackageName() != null);
-    }
-
     public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
             writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
@@ -1984,26 +679,13 @@
     }
 
     /**
-     * @return {@link FolderInfo} if its already loaded.
-     */
-    public FolderInfo findFolderById(Long folderId) {
-        synchronized (sBgDataModel) {
-            return sBgDataModel.folders.get(folderId);
-        }
-    }
-
-    @Thunk class DeferredMainThreadExecutor implements Executor {
-
-        @Override
-        public void execute(Runnable command) {
-            runOnMainThread(command);
-        }
-    }
-
-    /**
      * @return the looper for the worker thread which can be used to start background tasks.
      */
     public static Looper getWorkerLooper() {
         return sWorkerThread.getLooper();
     }
+
+    public static void setWorkerPriority(final int priority) {
+        Process.setThreadPriority(sWorkerThread.getThreadId(), priority);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 3150d5b..b31df98 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
@@ -38,6 +39,7 @@
 import android.database.sqlite.SQLiteStatement;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -53,43 +55,39 @@
 import com.android.launcher3.LauncherSettings.WorkspaceScreens;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dynamicui.ExtractionUtils;
 import com.android.launcher3.graphics.IconShapeOverride;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.DbDowngradeHelper;
 import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.NoLocaleSqliteContext;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Thunk;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.reflect.Method;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
     private static final boolean LOGD = false;
 
+    private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
+
     /**
      * Represents the schema of the database. Changes in scheme need not be backwards compatible.
      */
-    private static final int SCHEMA_VERSION = 27;
-    /**
-     * Represents the actual data. It could include additional validations and normalizations added
-     * overtime. These must be backwards compatible, else we risk breaking old devices during
-     * restore or binary version downgrade.
-     */
-    private static final int DATA_VERSION = 3;
+    public static final int SCHEMA_VERSION = 27;
 
-    private static final String PREF_KEY_DATA_VERISON = "provider_data_version";
-
-    public static final String AUTHORITY = ProviderConfig.AUTHORITY;
+    public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern();
 
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
@@ -114,7 +112,7 @@
 
     @Override
     public boolean onCreate() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD) {
             Log.d(TAG, "Launcher process started");
         }
         mListenerHandler = new Handler(mListenerWrapper);
@@ -305,8 +303,7 @@
         SqlArguments args = new SqlArguments(uri);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             int numValues = values.length;
             for (int i = 0; i < numValues; i++) {
                 addModifiedTime(values[i]);
@@ -314,9 +311,7 @@
                     return 0;
                 }
             }
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
+            t.commit();
         }
 
         notifyListeners();
@@ -328,15 +323,11 @@
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
         createDbIfNotExists();
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
+        try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
             ContentProviderResult[] result =  super.applyBatch(operations);
-            db.setTransactionSuccessful();
+            t.commit();
             reloadLauncherIfExternal();
             return result;
-        } finally {
-            db.endTransaction();
         }
     }
 
@@ -442,31 +433,26 @@
     private ArrayList<Long> deleteEmptyFolders() {
         ArrayList<Long> folderIds = new ArrayList<>();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             // Select folders whose id do not match any container value.
             String selection = LauncherSettings.Favorites.ITEM_TYPE + " = "
                     + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
                     + LauncherSettings.Favorites._ID +  " NOT IN (SELECT " +
                             LauncherSettings.Favorites.CONTAINER + " FROM "
                                 + Favorites.TABLE_NAME + ")";
-            Cursor c = db.query(Favorites.TABLE_NAME,
+            try (Cursor c = db.query(Favorites.TABLE_NAME,
                     new String[] {LauncherSettings.Favorites._ID},
-                    selection, null, null, null, null);
-            while (c.moveToNext()) {
-                folderIds.add(c.getLong(0));
+                    selection, null, null, null, null)) {
+                LauncherDbUtils.iterateCursor(c, 0, folderIds);
             }
-            c.close();
             if (!folderIds.isEmpty()) {
                 db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
                         LauncherSettings.Favorites._ID, folderIds), null);
             }
-            db.setTransactionSuccessful();
+            t.commit();
         } catch (SQLException ex) {
             Log.e(TAG, ex.getMessage(), ex);
             folderIds.clear();
-        } finally {
-            db.endTransaction();
         }
         return folderIds;
     }
@@ -566,7 +552,14 @@
     }
 
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
-        int defaultLayout = LauncherAppState.getIDP(getContext()).defaultLayoutId;
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
+        int defaultLayout = idp.defaultLayoutId;
+
+        UserManagerCompat um = UserManagerCompat.getInstance(getContext());
+        if (um.isDemoUser() && idp.demoModeLayoutId != 0) {
+            defaultLayout = idp.demoModeLayoutId;
+        }
+
         return new DefaultLayoutParser(getContext(), widgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
@@ -714,50 +707,30 @@
         @Override
         public void onOpen(SQLiteDatabase db) {
             super.onOpen(db);
-            SharedPreferences prefs = mContext
-                    .getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
-            int oldVersion = prefs.getInt(PREF_KEY_DATA_VERISON, 0);
-            if (oldVersion != DATA_VERSION) {
-                // Only run the data upgrade path for an existing db.
-                if (!Utilities.getPrefs(mContext).getBoolean(EMPTY_DATABASE_CREATED, false)) {
-                    db.beginTransaction();
-                    try {
-                        onDataUpgrade(db, oldVersion);
-                        db.setTransactionSuccessful();
-                    } catch (Exception e) {
-                        Log.d(TAG, "Error updating data version, ignoring", e);
-                        return;
-                    } finally {
-                        db.endTransaction();
-                    }
-                }
-                prefs.edit().putInt(PREF_KEY_DATA_VERISON, DATA_VERSION).apply();
+
+            File schemaFile = mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE);
+            if (!schemaFile.exists()) {
+                handleOneTimeDataUpgrade(db);
             }
+            DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext,
+                    R.raw.downgrade_schema);
         }
 
         /**
-         * Called when the data is updated as part of app update. It can be called multiple times
-         * with old version, even though it had been run before. The changes made here must be
-         * backwards compatible, else we risk breaking old devices during restore or binary
-         * version downgrade.
+         * One-time data updated before support of onDowngrade was added. This update is backwards
+         * compatible and can safely be run multiple times.
+         * Note: No new logic should be added here after release, as the new logic might not get
+         * executed on an existing device.
+         * TODO: Move this to db upgrade path, once the downgrade path is released.
          */
-        protected void onDataUpgrade(SQLiteDatabase db, int oldVersion) {
-            switch (oldVersion) {
-                case 0:
-                case 1: {
-                    // Remove "profile extra"
-                    UserManagerCompat um = UserManagerCompat.getInstance(mContext);
-                    for (UserHandle user : um.getUserProfiles()) {
-                        long serial = um.getSerialNumberForUser(user);
-                        String sql = "update favorites set intent = replace(intent, "
-                                + "';l.profile=" + serial + ";', ';') where itemType = 0;";
-                        db.execSQL(sql);
-                    }
-                }
-                case 2:
-                case 3:
-                    // data updated
-                    return;
+        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) {
+            // Remove "profile extra"
+            UserManagerCompat um = UserManagerCompat.getInstance(mContext);
+            for (UserHandle user : um.getUserProfiles()) {
+                long serial = um.getSerialNumberForUser(user);
+                String sql = "update favorites set intent = replace(intent, "
+                        + "';l.profile=" + serial + ";', ';') where itemType = 0;";
+                db.execSQL(sql);
             }
         }
 
@@ -774,35 +747,29 @@
                     addWorkspacesTable(db, false);
                 }
                 case 13: {
-                    db.beginTransaction();
-                    try {
+                    try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                         // Insert new column for holding widget provider name
                         db.execSQL("ALTER TABLE favorites " +
                                 "ADD COLUMN appWidgetProvider TEXT;");
-                        db.setTransactionSuccessful();
+                        t.commit();
                     } catch (SQLException ex) {
                         Log.e(TAG, ex.getMessage(), ex);
                         // Old version remains, which means we wipe old data
                         break;
-                    } finally {
-                        db.endTransaction();
                     }
                 }
                 case 14: {
-                    db.beginTransaction();
-                    try {
+                    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;");
-                        db.setTransactionSuccessful();
+                        t.commit();
                     } catch (SQLException ex) {
                         Log.e(TAG, ex.getMessage(), ex);
                         // Old version remains, which means we wipe old data
                         break;
-                    } finally {
-                        db.endTransaction();
                     }
                 }
                 case 15: {
@@ -870,29 +837,25 @@
 
         @Override
         public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion == 28 && newVersion == 27) {
-                // TODO: remove this check. This is only applicable for internal development/testing
-                // and for any released version of Launcher.
-                return;
+            try {
+                DbDowngradeHelper.parse(mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE))
+                        .onDowngrade(db, oldVersion, newVersion);
+            } catch (Exception e) {
+                Log.d(TAG, "Unable to downgrade from: " + oldVersion + " to " + newVersion +
+                        ". Wiping databse.", e);
+                createEmptyDB(db);
             }
-            // This shouldn't happen -- throw our hands up in the air and start over.
-            Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
-                    ". Wiping databse.");
-            createEmptyDB(db);
         }
 
         /**
          * Clears all the data for a fresh start.
          */
         public void createEmptyDB(SQLiteDatabase db) {
-            db.beginTransaction();
-            try {
+            try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                 db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
                 db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
                 onCreate(db);
-                db.setTransactionSuccessful();
-            } finally {
-                db.endTransaction();
+                t.commit();
             }
         }
 
@@ -900,40 +863,39 @@
          * Removes widgets which are registered to the Launcher's host, but are not present
          * in our model.
          */
+        @TargetApi(Build.VERSION_CODES.O)
         public void removeGhostWidgets(SQLiteDatabase db) {
             // Get all existing widget ids.
             final AppWidgetHost host = newLauncherWidgetHost();
             final int[] allWidgets;
             try {
-                Method getter = AppWidgetHost.class.getDeclaredMethod("getAppWidgetIds");
-                getter.setAccessible(true);
-                allWidgets = (int[]) getter.invoke(host);
-            } catch (Exception e) {
+                // Although the method was defined in O, it has existed since the beginning of time,
+                // so it might work on older platforms as well.
+                allWidgets = host.getAppWidgetIds();
+            } catch (IncompatibleClassChangeError e) {
                 Log.e(TAG, "getAppWidgetIds not supported", e);
                 return;
             }
-            try {
-                Cursor c = db.query(Favorites.TABLE_NAME,
-                        new String[] {Favorites.APPWIDGET_ID },
-                        "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null);
-                HashSet<Integer> validWidgets = new HashSet<>();
+            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));
                 }
-                c.close();
-
-                for (int widgetId : allWidgets) {
-                    if (!validWidgets.contains(widgetId)) {
-                        try {
-                            FileLog.d(TAG, "Deleting invalid widget " + widgetId);
-                            host.deleteAppWidgetId(widgetId);
-                        } catch (RuntimeException e) {
-                            // Ignore
-                        }
-                    }
-                }
             } catch (SQLException ex) {
                 Log.w(TAG, "Error getting widgets list", ex);
+                return;
+            }
+            for (int widgetId : allWidgets) {
+                if (!validWidgets.contains(widgetId)) {
+                    try {
+                        FileLog.d(TAG, "Deleting invalid widget " + widgetId);
+                        host.deleteAppWidgetId(widgetId);
+                    } catch (RuntimeException e) {
+                        // Ignore
+                    }
+                }
             }
         }
 
@@ -942,22 +904,16 @@
          * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
          */
         @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) {
-            db.beginTransaction();
-            Cursor c = null;
-            SQLiteStatement updateStmt = null;
-
-            try {
-                // Only consider the primary user as other users can't have a shortcut.
-                long userSerial = getDefaultUserSerial();
-                c = db.query(Favorites.TABLE_NAME, new String[] {
-                        Favorites._ID,
-                        Favorites.INTENT,
-                    }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
-                    null, null, null, null);
-
-                updateStmt = db.compileStatement("UPDATE favorites SET itemType="
-                        + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?");
-
+            try (SQLiteTransaction t = new SQLiteTransaction(db);
+                 // Only consider the primary user as other users can't have a shortcut.
+                 Cursor c = db.query(Favorites.TABLE_NAME,
+                         new String[] { Favorites._ID, Favorites.INTENT},
+                         "itemType=" + Favorites.ITEM_TYPE_SHORTCUT +
+                                 " AND profileId=" + getDefaultUserSerial(),
+                         null, null, null, null);
+                 SQLiteStatement updateStmt = db.compileStatement("UPDATE favorites SET itemType="
+                         + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?")
+            ) {
                 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
                 final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
 
@@ -979,17 +935,9 @@
                     updateStmt.bindLong(1, id);
                     updateStmt.executeUpdateDelete();
                 }
-                db.setTransactionSuccessful();
+                t.commit();
             } catch (SQLException ex) {
                 Log.w(TAG, "Error deduping shortcuts", ex);
-            } finally {
-                db.endTransaction();
-                if (c != null) {
-                    c.close();
-                }
-                if (updateStmt != null) {
-                    updateStmt.close();
-                }
             }
         }
 
@@ -997,26 +945,17 @@
          * Recreates workspace table and migrates data to the new table.
          */
         public boolean recreateWorkspaceTable(SQLiteDatabase db) {
-            db.beginTransaction();
-            try {
-                Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
+            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);
-                ArrayList<Long> sortedIDs = new ArrayList<Long>();
-                long maxId = 0;
-                try {
-                    while (c.moveToNext()) {
-                        Long id = c.getLong(0);
-                        if (!sortedIDs.contains(id)) {
-                            sortedIDs.add(id);
-                            maxId = Math.max(maxId, id);
-                        }
-                    }
-                } finally {
-                    c.close();
+                        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);
 
@@ -1029,21 +968,18 @@
                     addModifiedTime(values);
                     db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
                 }
-                db.setTransactionSuccessful();
-                mMaxScreenId = maxId;
+                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;
-            } finally {
-                db.endTransaction();
             }
             return true;
         }
 
         @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
-            db.beginTransaction();
-            try {
+            try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                 if (addRankColumn) {
                     // Insert new column for holding rank
                     db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;");
@@ -1062,13 +998,11 @@
                 }
 
                 c.close();
-                db.setTransactionSuccessful();
+                t.commit();
             } catch (SQLException ex) {
                 // Old version remains, which means we wipe old data
                 Log.e(TAG, ex.getMessage(), ex);
                 return false;
-            } finally {
-                db.endTransaction();
             }
             return true;
         }
@@ -1078,16 +1012,13 @@
         }
 
         private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
-            db.beginTransaction();
-            try {
+            try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                 db.execSQL("ALTER TABLE favorites ADD COLUMN "
                         + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";");
-                db.setTransactionSuccessful();
+                t.commit();
             } catch (SQLException ex) {
                 Log.e(TAG, ex.getMessage(), ex);
                 return false;
-            } finally {
-                db.endTransaction();
             }
             return true;
         }
@@ -1107,7 +1038,7 @@
         }
 
         public AppWidgetHost newLauncherWidgetHost() {
-            return new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID);
+            return new LauncherAppWidgetHost(mContext);
         }
 
         @Override
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 643d48a..a814323 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -11,6 +11,9 @@
 import android.view.View;
 import android.view.ViewDebug;
 
+import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW;
+
 public class LauncherRootView extends InsettableFrameLayout {
 
     private final Paint mOpaquePaint;
@@ -44,20 +47,28 @@
     @TargetApi(23)
     @Override
     protected boolean fitSystemWindows(Rect insets) {
-        boolean rawInsetsChanged = !mInsets.equals(insets);
         mDrawSideInsetBar = (insets.right > 0 || insets.left > 0) &&
                 (!Utilities.ATLEAST_MARSHMALLOW ||
-                getContext().getSystemService(ActivityManager.class).isLowRamDevice());
-        mRightInsetBarWidth = insets.right;
-        mLeftInsetBarWidth = insets.left;
-        setInsets(mDrawSideInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+                        getContext().getSystemService(ActivityManager.class).isLowRamDevice());
+        if (mDrawSideInsetBar) {
+            mLeftInsetBarWidth = insets.left;
+            mRightInsetBarWidth = insets.right;
+            insets = new Rect(0, insets.top, 0, insets.bottom);
+        } else {
+            mLeftInsetBarWidth = mRightInsetBarWidth = 0;
+        }
+        Launcher.getLauncher(getContext()).getSystemUiController().updateUiState(
+                UI_STATE_ROOT_VIEW, mDrawSideInsetBar ? FLAG_DARK_NAV : 0);
 
-        if (mAlignedView != null && mDrawSideInsetBar) {
+        boolean rawInsetsChanged = !mInsets.equals(insets);
+        setInsets(insets);
+
+        if (mAlignedView != null) {
             // Apply margins on aligned view to handle left/right insets.
             MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
-            if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
-                lp.leftMargin = insets.left;
-                lp.rightMargin = insets.right;
+            if (lp.leftMargin != mLeftInsetBarWidth || lp.rightMargin != mRightInsetBarWidth) {
+                lp.leftMargin = mLeftInsetBarWidth;
+                lp.rightMargin = mRightInsetBarWidth;
                 mAlignedView.setLayoutParams(lp);
             }
         }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index b25b256..87f62eb 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -22,8 +22,6 @@
 import android.os.Bundle;
 import android.provider.BaseColumns;
 
-import com.android.launcher3.config.ProviderConfig;
-
 /**
  * Settings related utilities.
  */
@@ -101,7 +99,7 @@
          * The content:// style URL for this table
          */
         public static final Uri CONTENT_URI = Uri.parse("content://" +
-                ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+                LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
 
         /**
          * The rank of this screen -- ie. how it is ordered relative to the other screens.
@@ -121,7 +119,7 @@
          * The content:// style URL for this table
          */
         public static final Uri CONTENT_URI = Uri.parse("content://" +
-                ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+                LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
 
         /**
          * The content:// style URL for a given row, identified by its id.
@@ -131,7 +129,7 @@
          * @return The unique content URL for the specified row.
          */
         public static Uri getContentUri(long id) {
-            return Uri.parse("content://" + ProviderConfig.AUTHORITY +
+            return Uri.parse("content://" + LauncherProvider.AUTHORITY +
                     "/" + TABLE_NAME + "/" + id);
         }
 
@@ -155,6 +153,18 @@
             }
         }
 
+        static final String itemTypeToString(int type) {
+            switch(type) {
+                case ITEM_TYPE_APPLICATION: return "APP";
+                case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
+                case ITEM_TYPE_FOLDER: return "FOLDER";
+                case ITEM_TYPE_APPWIDGET: return "WIDGET";
+                case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
+                case ITEM_TYPE_DEEP_SHORTCUT: return "DEEPSHORTCUT";
+                default: return String.valueOf(type);
+            }
+        }
+
         /**
          * The screen holding the favorite (if container is CONTAINER_DESKTOP)
          * <P>Type: INTEGER</P>
@@ -280,7 +290,7 @@
     public static final class Settings {
 
         public static final Uri CONTENT_URI = Uri.parse("content://" +
-                ProviderConfig.AUTHORITY + "/settings");
+                LauncherProvider.AUTHORITY + "/settings");
 
         public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
         public static final String METHOD_WAS_EMPTY_DB_CREATED = "get_empty_db_flag";
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 39c466d..e247490 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -31,8 +31,8 @@
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
 
@@ -80,13 +80,11 @@
 public class LauncherStateTransitionAnimation {
 
     /**
-     * animation used for all apps and widget tray when
-     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false}
+     * animation used for the widget tray
      */
     public static final int CIRCULAR_REVEAL = 0;
     /**
-     * animation used for all apps and not widget tray when
-     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true}
+     * animation used for all apps tray
      */
     public static final int PULLUP = 1;
 
@@ -127,12 +125,8 @@
 
     /**
      * Starts an animation to the apps view.
-     *
-     * @param startSearchAfterTransition Immediately starts app search after the transition to
-     *                                   All Apps is completed.
      */
-    public void startAnimationToAllApps(
-            final boolean animated, final boolean startSearchAfterTransition) {
+    public void startAnimationToAllApps(final boolean animated) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
         final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@@ -156,18 +150,11 @@
             @Override
             void onTransitionComplete() {
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
-                if (startSearchAfterTransition) {
-                    toView.startAppsSearch();
-                }
             }
         };
-        int animType = CIRCULAR_REVEAL;
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            animType = PULLUP;
-        }
         // Only animate the search bar if animating from spring loaded mode back to all apps
         startAnimationToOverlay(
-                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
+                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb);
     }
 
     /**
@@ -200,12 +187,8 @@
 
         if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
                 || mAllAppsController.isTransitioning()) {
-            int animType = CIRCULAR_REVEAL;
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-                animType = PULLUP;
-            }
             startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
-                    animated, animType, onCompleteRunnable);
+                    animated, PULLUP, onCompleteRunnable);
         } else if (fromState == Launcher.State.WIDGETS ||
                 fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
             startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
@@ -242,8 +225,7 @@
         playCommonTransitionAnimations(toWorkspaceState,
                 animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                    toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+            if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
                 mAllAppsController.finishPullUp();
             }
             toView.setTranslationX(0.0f);
@@ -345,8 +327,10 @@
             toView.post(new StartAnimRunnable(animation, toView));
             mCurrentAnimation = animation;
         } else if (animType == PULLUP) {
-            // We are animating the content view alpha, so ensure we have a layer for it
-            layerViews.addView(contentView);
+            if (!FeatureFlags.LAUNCHER3_PHYSICS) {
+                // We are animating the content view alpha, so ensure we have a layer for it.
+                layerViews.addView(contentView);
+            }
 
             animation.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -394,7 +378,6 @@
     private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
             final Workspace.State toWorkspaceState, final boolean animated, int type,
             final Runnable onCompleteRunnable) {
-        final AllAppsContainerView appsView = mLauncher.getAppsView();
         // No alpha anim from all apps
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
             @Override
@@ -424,12 +407,11 @@
             @Override
             void onTransitionComplete() {
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
-                appsView.reset();
             }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
         startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
-                mLauncher.getStartViewForAllAppsRevealAnimation(), appsView,
+                mLauncher.getStartViewForAllAppsRevealAnimation(), mLauncher.getAppsView(),
                 animated, type, onCompleteRunnable, cb);
     }
 
@@ -534,8 +516,7 @@
         playCommonTransitionAnimations(toWorkspaceState,
                 animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                    fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+            if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
                 mAllAppsController.finishPullDown();
             }
             fromView.setVisibility(View.GONE);
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
index 4ca0a59..5094682 100644
--- a/src/com/android/launcher3/MainThreadExecutor.java
+++ b/src/com/android/launcher3/MainThreadExecutor.java
@@ -18,14 +18,14 @@
 
 import android.os.Looper;
 
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
 
 /**
  * An executor service that executes its tasks on the main thread.
  *
  * Shutting down this executor is not supported.
  */
-public class MainThreadExecutor extends LooperExecuter {
+public class MainThreadExecutor extends LooperExecutor {
 
     public MainThreadExecutor() {
         super(Looper.getMainLooper());
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fb6a611..87f3dda 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -49,7 +48,8 @@
 
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.LauncherEdgeEffect;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -69,6 +69,9 @@
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
     protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
+    // OverScroll constants
+    private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
+
     private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
     // The page is moved more than halfway, automatically move to the next page on touch up.
     private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
@@ -144,6 +147,13 @@
 
     protected boolean mWasInOverscroll = false;
 
+    // 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 int mUnboundedScrollX;
+
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     protected PageIndicator mPageIndicator;
@@ -177,16 +187,11 @@
     // Convenience/caching
     private static final Matrix sTmpInvMatrix = new Matrix();
     private static final float[] sTmpPoint = new float[2];
-    private static final int[] sTmpIntPoint = new int[2];
     private static final Rect sTmpRect = new Rect();
 
     protected final Rect mInsets = new Rect();
     protected final boolean mIsRtl;
 
-    // Edge effect
-    private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
-    private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
-
     public PagedView(Context context) {
         this(context, null);
     }
@@ -228,11 +233,6 @@
         setWillNotDraw(false);
     }
 
-    protected void setEdgeGlowColor(int color) {
-        mEdgeGlowLeft.setColor(color);
-        mEdgeGlowRight.setColor(color);
-    }
-
     protected void setDefaultInterpolator(Interpolator interpolator) {
         mDefaultInterpolator = interpolator;
         mScroller.setInterpolator(mDefaultInterpolator);
@@ -404,9 +404,10 @@
         if (getChildCount() == 0) {
             return;
         }
+        int prevPage = mCurrentPage;
         mCurrentPage = validateNewPage(currentPage);
         updateCurrentPageScroll();
-        notifyPageSwitchListener();
+        notifyPageSwitchListener(prevPage);
         invalidate();
     }
 
@@ -414,7 +415,7 @@
      * Should be called whenever the page changes. In the case of a scroll, we wait until the page
      * has settled.
      */
-    protected void notifyPageSwitchListener() {
+    protected void notifyPageSwitchListener(int prevPage) {
         updatePageIndicator();
     }
 
@@ -476,7 +477,7 @@
     }
 
     protected int getUnboundedScrollX() {
-        return getScrollX();
+        return mUnboundedScrollX;
     }
 
     @Override
@@ -499,6 +500,8 @@
             x = Math.max(x, mFreeScrollMinScrollX);
         }
 
+        mUnboundedScrollX = x;
+
         boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
         boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
         if (isXBeforeFirstPage) {
@@ -526,6 +529,7 @@
                 overScroll(0);
                 mWasInOverscroll = false;
             }
+            mOverScrollX = x;
             super.scrollTo(x, y);
         }
 
@@ -565,7 +569,8 @@
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
             if (getUnboundedScrollX() != mScroller.getCurrX()
-                    || getScrollY() != mScroller.getCurrY()) {
+                    || getScrollY() != mScroller.getCurrY()
+                    || mOverScrollX != mScroller.getCurrX()) {
                 float scaleX = mFreeScroll ? getScaleX() : 1f;
                 int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX));
                 scrollTo(scrollX, mScroller.getCurrY());
@@ -577,9 +582,10 @@
         } else if (mNextPage != INVALID_PAGE && shouldInvalidate) {
             sendScrollAccessibilityEvent();
 
+            int prevPage = mCurrentPage;
             mCurrentPage = validateNewPage(mNextPage);
             mNextPage = INVALID_PAGE;
-            notifyPageSwitchListener();
+            notifyPageSwitchListener(prevPage);
 
             // We don't want to trigger a page end moving unless the page has settled
             // and the user has stopped scrolling
@@ -916,6 +922,7 @@
     @Override
     public void onChildViewRemoved(View parent, View child) {
         updateFreescrollBounds();
+        mCurrentPage = validateNewPage(mCurrentPage);
         invalidate();
     }
 
@@ -973,47 +980,6 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (getPageCount() > 0) {
-            if (!mEdgeGlowLeft.isFinished()) {
-                final int restoreCount = canvas.save();
-                Rect display = mViewport;
-                canvas.translate(display.left, display.top);
-                canvas.rotate(270);
-
-                getEdgeVerticalPosition(sTmpIntPoint);
-                canvas.translate(display.top - sTmpIntPoint[1], 0);
-                mEdgeGlowLeft.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
-                if (mEdgeGlowLeft.draw(canvas)) {
-                    postInvalidateOnAnimation();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowRight.isFinished()) {
-                final int restoreCount = canvas.save();
-                Rect display = mViewport;
-                canvas.translate(display.left + mPageScrolls[mIsRtl ? 0 : (getPageCount() - 1)], display.top);
-                canvas.rotate(90);
-
-                getEdgeVerticalPosition(sTmpIntPoint);
-
-                canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
-                mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
-                if (mEdgeGlowRight.draw(canvas)) {
-                    postInvalidateOnAnimation();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
-    }
-
-    /**
-     * Returns the top and bottom position for the edge effect.
-     */
-    protected abstract void getEdgeVerticalPosition(int[] pos);
-
-    @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         int page = indexToPage(indexOfChild(child));
         if (page != mCurrentPage || !mScroller.isFinished()) {
@@ -1355,14 +1321,15 @@
     }
 
     protected void dampedOverScroll(float amount) {
-        int screenSize = getViewportWidth();
-        float f = (amount / screenSize);
-        if (f < 0) {
-            mEdgeGlowLeft.onPull(-f);
-        } else if (f > 0) {
-            mEdgeGlowRight.onPull(f);
+        if (Float.compare(amount, 0f) == 0) return;
+
+        int overScrollAmount = OverScroll.dampedScroll(amount, getViewportWidth());
+        if (amount < 0) {
+            mOverScrollX = overScrollAmount;
+            super.scrollTo(mOverScrollX, getScrollY());
         } else {
-            return;
+            mOverScrollX = mMaxScrollX + overScrollAmount;
+            super.scrollTo(mOverScrollX, getScrollY());
         }
         invalidate();
     }
@@ -1599,7 +1566,7 @@
                 mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
 
                 boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
-                        Math.abs(velocityX) > mFlingThresholdVelocity;
+                        shouldFlingForVelocity(velocityX);
 
                 if (!mFreeScroll) {
                     // In the case that the page is moved far to one direction and then is flung
@@ -1705,14 +1672,16 @@
         return true;
     }
 
+    protected boolean shouldFlingForVelocity(int velocityX) {
+        return Math.abs(velocityX) > mFlingThresholdVelocity;
+    }
+
     private void resetTouchState() {
         releaseVelocityTracker();
         endReordering();
         mCancelTap = false;
         mTouchState = TOUCH_STATE_REST;
         mActivePointerId = INVALID_POINTER;
-        mEdgeGlowLeft.onRelease();
-        mEdgeGlowRight.onRelease();
     }
 
     /**
@@ -1826,7 +1795,18 @@
     }
 
     protected void snapToDestination() {
-        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+    }
+
+    protected boolean isInOverScroll() {
+        return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+    }
+
+    protected int getPageSnapDuration() {
+        if (isInOverScroll()) {
+            return OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION;
+        }
+        return PAGE_SNAP_ANIMATION_DURATION;
     }
 
     public static class ScrollInterpolator implements Interpolator {
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index b163464..c2d5501 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -80,10 +80,13 @@
         updateAppWidget(null);
         setOnClickListener(mLauncher);
 
-        // Load icon
-        PackageItemInfo item = new PackageItemInfo(info.providerName.getPackageName());
-        item.user = info.user;
-        cache.updateIconInBackground(this, item);
+        if (info.pendingItemInfo == null) {
+            info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
+            info.pendingItemInfo.user = info.user;
+            cache.updateIconInBackground(this, info.pendingItemInfo);
+        } else {
+            reapplyItemInfo(info.pendingItemInfo);
+        }
     }
 
     @Override
@@ -108,7 +111,7 @@
     }
 
     @Override
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re inflate is required any time the widget restore status changes
         return mStartState != mInfo.restoreStatus;
     }
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index f8196e5..c3d3bb3 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -56,11 +56,10 @@
     private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
 
     private static final int INDEX_HOTSEAT = 0;
-    private static final int INDEX_QSB = 1;
-    private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
-    private static final int INDEX_SCRIM = 3;
+    private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1;
+    private static final int INDEX_SCRIM = 2;
 
-    private final Animator[] mAnimators = new Animator[4];
+    private final Animator[] mAnimators = new Animator[3];
 
     private Launcher mLauncher;
     private Workspace mWorkspace;
@@ -196,8 +195,6 @@
     private void animateHotseatAndQsb(boolean show) {
         startAnimator(INDEX_HOTSEAT,
                 mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
-        startAnimator(INDEX_QSB, mWorkspace.mQsbAlphaController.animateAlphaAtIndex(
-                show ? 1 : 0, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE), THRESHOLD_ANIM_DURATION);
     }
 
     private void animateOverviewPanelButtons(boolean show) {
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
new file mode 100644
index 0000000..07515d0
--- /dev/null
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import android.content.Intent;
+import android.support.annotation.NonNull;
+
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.util.PackageManagerHelper;
+
+public class PromiseAppInfo extends AppInfo {
+
+    public int level = 0;
+
+    public PromiseAppInfo(@NonNull PackageInstallerCompat.PackageInstallInfo installInfo) {
+        componentName = installInfo.componentName;
+        intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setComponent(componentName)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+    }
+
+    @Override
+    public ShortcutInfo makeShortcut() {
+        ShortcutInfo shortcut = new ShortcutInfo(this);
+        shortcut.setInstallProgress(level);
+        // We need to update the component name when the apk is installed
+        shortcut.status |= ShortcutInfo.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;
+        return shortcut;
+    }
+
+    public Intent getMarketIntent() {
+        return PackageManagerHelper.getMarketIntent(componentName.getPackageName());
+    }
+}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 61bcc17..edb7ff5 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -59,7 +59,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!isEnabled(context) || !Utilities.isAtLeastO()) {
+        if (!isEnabled(context) || !Utilities.ATLEAST_OREO) {
             // User has decided to not add icons on homescreen.
             return;
         }
@@ -67,18 +67,19 @@
         SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
         UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
 
-        if (TextUtils.isEmpty(info.getAppPackageName()) ||
-                info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
-            return;
+        if (Process.myUserHandle().equals(user)) {
+            if (TextUtils.isEmpty(info.getAppPackageName()) ||
+                    info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
+                return;
+            }
         }
 
-        if (!Process.myUserHandle().equals(user)) {
-            // Managed profile is handled using ManagedProfileHeuristic
-            return;
-        }
+        queueAppIconAddition(context, info.getAppPackageName(), user);
+    }
 
+    public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
         List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
-                .getActivityList(info.getAppPackageName(), user);
+                .getActivityList(packageName, user);
         if (activities == null || activities.isEmpty()) {
             // no activity found
             return;
@@ -91,7 +92,7 @@
     }
 
     public static void applyDefaultUserPrefs(final Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         SharedPreferences prefs = Utilities.getPrefs(context);
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 0902b20..d40ac8f 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -17,18 +17,25 @@
 package com.android.launcher3;
 
 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.database.ContentObserver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
 import android.os.Bundle;
-import android.os.Handler;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.provider.Settings;
-import android.provider.Settings.System;
-import android.support.v4.os.BuildCompat;
 
 import com.android.launcher3.graphics.IconShapeOverride;
+import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.SettingsObserver;
+import com.android.launcher3.views.ButtonPreference;
 
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -36,17 +43,21 @@
 public class SettingsActivity extends Activity {
 
     private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
-    // TODO: use Settings.Secure.NOTIFICATION_BADGING
-    private static final String NOTIFICATION_BADGING = "notification_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";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        // Display the fragment as the main content.
-        getFragmentManager().beginTransaction()
-                .replace(android.R.id.content, new LauncherSettingsFragment())
-                .commit();
+        if (savedInstanceState == null) {
+            // Display the fragment as the main content.
+            getFragmentManager().beginTransaction()
+                    .replace(android.R.id.content, new LauncherSettingsFragment())
+                    .commit();
+        }
     }
 
     /**
@@ -75,27 +86,25 @@
 
                 // Register a content observer to listen for system setting changes while
                 // this UI is active.
-                resolver.registerContentObserver(
-                        Settings.System.getUriFor(System.ACCELEROMETER_ROTATION),
-                        false, mRotationLockObserver);
+                mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION);
 
                 // Initialize the UI once
-                mRotationLockObserver.onChange(true);
                 rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
             }
 
-            Preference iconBadgingPref = findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!BuildCompat.isAtLeastO()) {
+            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);
-                resolver.registerContentObserver(
-                        Settings.Secure.getUriFor(NOTIFICATION_BADGING),
-                        false, mIconBadgingObserver);
-                mIconBadgingObserver.onChange(true);
+                mIconBadgingObserver = new IconBadgingObserver(
+                        iconBadgingPref, resolver, getFragmentManager());
+                mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
             }
 
             Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
@@ -111,11 +120,11 @@
         @Override
         public void onDestroy() {
             if (mRotationLockObserver != null) {
-                getActivity().getContentResolver().unregisterContentObserver(mRotationLockObserver);
+                mRotationLockObserver.unregister();
                 mRotationLockObserver = null;
             }
             if (mIconBadgingObserver != null) {
-                getActivity().getContentResolver().unregisterContentObserver(mIconBadgingObserver);
+                mIconBadgingObserver.unregister();
                 mIconBadgingObserver = null;
             }
             super.onDestroy();
@@ -126,22 +135,18 @@
      * Content observer which listens for system auto-rotate setting changes, and enables/disables
      * the launcher rotation setting accordingly.
      */
-    private static class SystemDisplayRotationLockObserver extends ContentObserver {
+    private static class SystemDisplayRotationLockObserver extends SettingsObserver.System {
 
         private final Preference mRotationPref;
-        private final ContentResolver mResolver;
 
         public SystemDisplayRotationLockObserver(
                 Preference rotationPref, ContentResolver resolver) {
-            super(new Handler());
+            super(resolver);
             mRotationPref = rotationPref;
-            mResolver = resolver;
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            boolean enabled = Settings.System.getInt(mResolver,
-                    Settings.System.ACCELEROMETER_ROTATION, 1) == 1;
+        public void onSettingChanged(boolean enabled) {
             mRotationPref.setEnabled(enabled);
             mRotationPref.setSummary(enabled
                     ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
@@ -152,24 +157,75 @@
      * Content observer which listens for system badging setting changes,
      * and updates the launcher badging setting subtext accordingly.
      */
-    private static class IconBadgingObserver extends ContentObserver {
+    private static class IconBadgingObserver extends SettingsObserver.Secure
+            implements Preference.OnPreferenceClickListener {
 
-        private final Preference mBadgingPref;
+        private final ButtonPreference mBadgingPref;
         private final ContentResolver mResolver;
+        private final FragmentManager mFragmentManager;
 
-        public IconBadgingObserver(Preference badgingPref, ContentResolver resolver) {
-            super(new Handler());
+        public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
+                FragmentManager fragmentManager) {
+            super(resolver);
             mBadgingPref = badgingPref;
             mResolver = resolver;
+            mFragmentManager = fragmentManager;
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            boolean enabled = Settings.Secure.getInt(mResolver, NOTIFICATION_BADGING, 1) == 1;
-            mBadgingPref.setSummary(enabled
-                    ? R.string.icon_badging_desc_on
-                    : R.string.icon_badging_desc_off);
+        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);
+            Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .putExtra(":settings:fragment_args_key", cn.flattenToString());
+            getActivity().startActivity(intent);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index f8742f8..a7e68ff 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -101,7 +101,7 @@
         mInvertIfRtl = invert;
     }
 
-    int getCellContentHeight() {
+    public int getCellContentHeight() {
         return Math.min(getMeasuredHeight(),
                 mLauncher.getDeviceProfile().getCellHeight(mContainerType));
     }
@@ -120,7 +120,9 @@
                 // Center the icon/folder
                 int cHeight = getCellContentHeight();
                 int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
-                int cellPaddingX = (int) (profile.edgeMarginPx / 2f);
+                int cellPaddingX = mContainerType == CellLayout.WORKSPACE
+                        ? profile.workspaceCellPaddingXPx
+                        : (int) (profile.edgeMarginPx / 2f);
                 child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
             }
         } else {
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 6f0417c..adf008b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.annotation.TargetApi;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -45,10 +46,10 @@
      * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
      * parsing.
      */
-    public static final int FLAG_AUTOINTALL_ICON = 2; //0B10;
+    public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
 
     /**
-     * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINTALL_ICON}
+     * 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;
@@ -59,6 +60,11 @@
     public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
 
     /**
+     * Web UI supported.
+     */
+    public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
+
+    /**
      * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
      * Upto 15 different types supported.
      */
@@ -185,7 +191,11 @@
 
 
     public final boolean isPromise() {
-        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
+        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
+    }
+
+    public boolean hasPromiseIconUi() {
+        return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
     }
 
     public int getInstallProgress() {
@@ -226,4 +236,18 @@
     public boolean isDisabled() {
         return isDisabled != 0;
     }
+
+    @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/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0fac29f..902fd34 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -11,12 +11,18 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.widget.Toast;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 
+import java.net.URISyntaxException;
+
 public class UninstallDropTarget extends ButtonDropTarget {
 
+    private static final String TAG = "UninstallDropTarget";
+    private static Boolean sUninstallDisabled;
+
     public UninstallDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -28,10 +34,13 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        setupUi();
+    }
+
+    protected void setupUi() {
         // Get the hover color
         mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
-
-        setDrawable(R.drawable.ic_uninstall_launcher);
+        setDrawable(R.drawable.ic_uninstall_shadow);
     }
 
     @Override
@@ -39,35 +48,36 @@
         return supportsDrop(getContext(), info);
     }
 
-    public static boolean supportsDrop(Context context, Object info) {
-        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bundle restrictions = userManager.getUserRestrictions();
-        if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
-                || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
+    public static boolean supportsDrop(Context context, ItemInfo info) {
+        if (sUninstallDisabled == null) {
+            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            Bundle restrictions = userManager.getUserRestrictions();
+            sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+                    || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
+        }
+        if (sUninstallDisabled) {
             return false;
         }
 
+        if (info instanceof AppInfo) {
+            AppInfo appInfo = (AppInfo) info;
+            if (appInfo.isSystemApp != AppInfo.FLAG_SYSTEM_UNKNOWN) {
+                return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
+            }
+        }
         return getUninstallTarget(context, info) != null;
     }
 
     /**
      * @return the component name that should be uninstalled or null.
      */
-    private static ComponentName getUninstallTarget(Context context, Object item) {
+    private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
         Intent intent = null;
         UserHandle user = null;
-        if (item instanceof AppInfo) {
-            AppInfo info = (AppInfo) item;
-            intent = info.intent;
-            user = info.user;
-        } else if (item instanceof ShortcutInfo) {
-            ShortcutInfo info = (ShortcutInfo) item;
-            if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
-                // Do not use restore/target intent here as we cannot uninstall an app which is
-                // being installed/restored.
-                intent = info.intent;
-                user = info.user;
-            }
+        if (item != null &&
+                item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
+            intent = item.getIntent();
+            user = item.user;
         }
         if (intent != null) {
             LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
@@ -104,25 +114,28 @@
             final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
         final ComponentName cn = getUninstallTarget(launcher, info);
 
-        final boolean isUninstallable;
+        boolean canUninstall;
         if (cn == null) {
             // System applications cannot be installed. For now, show a toast explaining that.
             // We may give them the option of disabling apps this way.
             Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
-            isUninstallable = false;
+            canUninstall = false;
         } else {
-            Intent intent = new Intent(Intent.ACTION_DELETE,
-                    Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            intent.putExtra(Intent.EXTRA_USER, info.user);
-            launcher.startActivity(intent);
-            isUninstallable = true;
+            try {
+                Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
+                        .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+                        .putExtra(Intent.EXTRA_USER, info.user);
+                launcher.startActivity(i);
+                canUninstall = true;
+            } catch (URISyntaxException e) {
+                Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
+                canUninstall = false;
+            }
         }
         if (callback != null) {
-            sendUninstallResult(launcher, isUninstallable, cn, info.user, callback);
+            sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
         }
-        return isUninstallable;
+        return canUninstall;
     }
 
     /**
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b5c44bb..b6876f6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -37,7 +37,6 @@
 import android.os.DeadObjectException;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
-import android.support.v4.os.BuildCompat;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -51,7 +50,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
@@ -84,15 +83,17 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
-    public static boolean isAtLeastO() {
-        return BuildCompat.isAtLeastO();
-    }
+    public static final boolean ATLEAST_OREO_MR1 =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
+
+    public static final boolean ATLEAST_OREO =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
 
     public static final boolean ATLEAST_NOUGAT_MR1 =
-        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
 
     public static final boolean ATLEAST_NOUGAT =
-        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
 
     public static final boolean ATLEAST_MARSHMALLOW =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
@@ -110,6 +111,7 @@
     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;
 
     // These values are same as that in {@link AsyncTask}.
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@@ -260,7 +262,7 @@
         return scale;
     }
 
-    static boolean isSystemApp(Context context, Intent intent) {
+    public static boolean isSystemApp(Context context, Intent intent) {
         PackageManager pm = context.getPackageManager();
         ComponentName cn = intent.getComponent();
         String packageName = null;
@@ -550,6 +552,11 @@
                 LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
+    public static SharedPreferences getDevicePrefs(Context context) {
+        return context.getSharedPreferences(
+                LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
+    }
+
     public static boolean isPowerSaverOn(Context context) {
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         return powerManager.isPowerSaveMode();
@@ -571,7 +578,7 @@
             try {
                 c.close();
             } catch (IOException e) {
-                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_DOGFOOD_BUILD) {
                     Log.d(TAG, "Error closing", e);
                 }
             }
@@ -647,4 +654,5 @@
         hashSet.add(elem);
         return hashSet;
     }
+
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index c525cd4..a65ea9b 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -27,7 +27,6 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 import android.util.LongSparseArray;
 
@@ -112,7 +111,7 @@
      * sizes (landscape vs portrait).
      */
     private static class CacheDb extends SQLiteCacheHelper {
-        private static final int DB_VERSION = 6;
+        private static final int DB_VERSION = 9;
 
         private static final String TABLE_NAME = "shortcut_and_widget_previews";
         private static final String COLUMN_COMPONENT = "componentName";
@@ -388,10 +387,10 @@
             drawable.setBounds(x, 0, x + previewWidth, previewHeight);
             drawable.draw(c);
         } else {
-            final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
-            RectF boxRect = drawBoxWithShadow(c, p, previewWidth, previewHeight);
+            RectF boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
 
             // Draw horizontal and vertical lines to represent individual columns.
+            final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
             p.setStyle(Paint.Style.STROKE);
             p.setStrokeWidth(mContext.getResources()
                     .getDimension(R.dimen.widget_preview_cell_divider_width));
@@ -431,28 +430,19 @@
         return preview;
     }
 
-    private RectF drawBoxWithShadow(Canvas c, Paint p, int width, int height) {
+    private RectF drawBoxWithShadow(Canvas c, int width, int height) {
         Resources res = mContext.getResources();
-        float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
-        float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
-        float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
 
-        RectF bounds = new RectF(shadowBlur, shadowBlur,
-                width - shadowBlur, height - shadowBlur - keyShadowDistance);
-        p.setColor(Color.WHITE);
+        ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.WHITE);
+        builder.shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
+        builder.radius = res.getDimension(R.dimen.widget_preview_corner_radius);
+        builder.keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
 
-        // Key shadow
-        p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
-                ShadowGenerator.KEY_SHADOW_ALPHA << 24);
-        c.drawRoundRect(bounds, corner, corner, p);
-
-        // Ambient shadow
-        p.setShadowLayer(shadowBlur, 0, 0,
-                ColorUtils.setAlphaComponent(Color.BLACK, ShadowGenerator.AMBIENT_SHADOW_ALPHA));
-        c.drawRoundRect(bounds, corner, corner, p);
-
-        p.clearShadowLayer();
-        return bounds;
+        builder.bounds.set(builder.shadowBlur, builder.shadowBlur,
+                width - builder.shadowBlur,
+                height - builder.shadowBlur - builder.keyShadowDistance);
+        builder.drawShadow(c);
+        return builder.bounds;
     }
 
     private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
@@ -478,16 +468,16 @@
             c.setBitmap(preview);
             c.drawColor(0, PorterDuff.Mode.CLEAR);
         }
-        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        RectF boxRect = drawBoxWithShadow(c, p, size, size);
+        RectF boxRect = drawBoxWithShadow(c, size, size);
 
         Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
-                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, Build.VERSION_CODES.O);
+                mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, 0);
         Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
 
         boxRect.set(0, 0, iconSize, iconSize);
         boxRect.offset(padding, padding);
-        c.drawBitmap(icon, src, boxRect, p);
+        c.drawBitmap(icon, src, boxRect,
+                new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
         c.setBitmap(null);
         return preview;
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 36f2880..37ee6d7 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 import android.util.Property;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
@@ -50,11 +51,11 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
+import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
@@ -64,7 +65,6 @@
 import com.android.launcher3.badge.FolderBadgeInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -72,15 +72,16 @@
 import com.android.launcher3.dragndrop.SpringLoadedDragController;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+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.ItemInfoMatcher;
 import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.MultiStateAlphaController;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.VerticalFlingDetector;
@@ -111,7 +112,7 @@
      * {@link #isFinishedSwitchingState()} ()} to return true. */
     private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
 
-    private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
+    private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
 
     private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
     private static final int FADE_EMPTY_SCREEN_DURATION = 150;
@@ -137,8 +138,8 @@
 
     private ShortcutAndWidgetContainer mDragSourceInternal;
 
-    @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
-    @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
+    @Thunk final LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
+    @Thunk final ArrayList<Long> mScreenOrder = new ArrayList<>();
 
     @Thunk Runnable mRemoveEmptyScreenRunnable;
     @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
@@ -174,7 +175,7 @@
      */
     private CellLayout mDropToLayout = null;
 
-    @Thunk Launcher mLauncher;
+    @Thunk final Launcher mLauncher;
     @Thunk DragController mDragController;
 
     // These are temporary variables to prevent having to allocate a new object just to
@@ -183,10 +184,10 @@
 
     private final int[] mTempXY = new int[2];
     @Thunk float[] mDragViewVisualCenter = new float[2];
-    private float[] mTempTouchCoordinates = new float[2];
+    private final float[] mTempTouchCoordinates = new float[2];
 
     private SpringLoadedDragController mSpringLoadedDragController;
-    private float mOverviewModeShrinkFactor;
+    private final float mOverviewModeShrinkFactor;
 
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
@@ -226,21 +227,13 @@
     /**
      * These values correspond to {@link Direction#X} & {@link Direction#Y}
      */
-    private float[] mPageAlpha = new float[] {1, 1};
+    private final float[] mPageAlpha = new float[] {1, 1};
     /**
      * Hotseat alpha can be changed when moving horizontally, vertically, changing states.
      * The values correspond to {@link Direction#X}, {@link Direction#Y} &
      * {@link #HOTSEAT_STATE_ALPHA_INDEX} respectively.
      */
-    private float[] mHotseatAlpha = new float[] {1, 1, 1};
-
-    public static final int QSB_ALPHA_INDEX_STATE_CHANGE = 0;
-    public static final int QSB_ALPHA_INDEX_Y_TRANSLATION = 1;
-    public static final int QSB_ALPHA_INDEX_PAGE_SCROLL = 2;
-    public static final int QSB_ALPHA_INDEX_OVERLAY_SCROLL = 3;
-
-
-    MultiStateAlphaController mQsbAlphaController;
+    private final float[] mHotseatAlpha = new float[] {1, 1, 1};
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.NORMAL;
@@ -252,7 +245,7 @@
     private boolean mStripScreensOnPageStopMoving = false;
 
     private DragPreviewProvider mOutlineProvider = null;
-    private boolean mWorkspaceFadeInAdjacentScreens;
+    private final boolean mWorkspaceFadeInAdjacentScreens;
 
     final WallpaperOffsetInterpolator mWallpaperOffset;
     private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -265,7 +258,7 @@
     public static final int REORDER_TIMEOUT = 350;
     private final Alarm mFolderCreationAlarm = new Alarm();
     private final Alarm mReorderAlarm = new Alarm();
-    private FolderIcon.PreviewBackground mFolderCreateBg;
+    private PreviewBackground mFolderCreateBg;
     private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
     private boolean mAddToExistingFolderOnDrop = false;
@@ -297,7 +290,7 @@
     @Thunk int mLastReorderY = -1;
 
     private SparseArray<Parcelable> mSavedStates;
-    private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
+    private final ArrayList<Integer> mRestoredPages = new ArrayList<>();
 
     private float mCurrentScale;
     private float mTransitionProgress;
@@ -311,19 +304,16 @@
     boolean mScrollInteractionBegan;
     boolean mStartedSendingScrollEvents;
     float mLastOverlayScroll = 0;
-    // Total over scrollX in the overlay direction.
-    private int mUnboundedScrollX;
+    boolean mOverlayShown = false;
+
     private boolean mForceDrawAdjacentPages = false;
     // Total over scrollX in the overlay direction.
     private float mOverlayTranslation;
-    private int mFirstPageScrollX;
-    private boolean mIgnoreQsbScroll;
 
     // Handles workspace state transitions
-    private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
+    private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
     private AccessibilityDelegate mPagesAccessibilityDelegate;
-    private OnStateChangeListener mOnStateChangeListener;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -378,10 +368,6 @@
         }
     }
 
-    public void setOnStateChangeListener(OnStateChangeListener listener) {
-        mOnStateChangeListener = listener;
-    }
-
     /**
      * Estimates the size of an item using spans: hSpan, vSpan.
      *
@@ -451,7 +437,7 @@
         mLauncher.lockScreenOrientation();
         mLauncher.onInteractionBegin();
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
-        InstallShortcutReceiver.enableInstallQueue();
+        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
 
         // Do not add a new page if it is a accessible drag which was not started by the workspace.
         // We do not support accessibility drag from other sources and instead provide a direct
@@ -504,7 +490,8 @@
         mLauncher.unlockScreenOrientation(false);
 
         // Re-enable any Un/InstallShortcutReceiver and now process any queued items
-        InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
+        InstallShortcutReceiver.disableAndFlushInstallQueue(
+                InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext());
 
         mOutlineProvider = null;
         mDragInfo = null;
@@ -529,15 +516,12 @@
 
         // Set the wallpaper dimensions when Launcher starts up
         setWallpaperDimension();
-
-        setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
     }
 
     @Override
     public void initParentViews(View parent) {
         super.initParentViews(parent);
         mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
-        mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 4);
     }
 
     private int getDefaultPage() {
@@ -577,11 +561,6 @@
         return mTouchState != TOUCH_STATE_REST;
     }
 
-    private int getEmbeddedQsbId() {
-        return mLauncher.getDeviceProfile().isVerticalBarLayout()
-                ? R.id.qsb_container : R.id.workspace_blocked_row;
-    }
-
     /**
      * Initializes and binds the first page
      * @param qsb an existing qsb to recycle or null.
@@ -623,41 +602,17 @@
         if (qsb == null) {
             // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
             // edges, we do not need a full width QSB.
-            qsb = mLauncher.getLayoutInflater().inflate(
-                    mLauncher.getDeviceProfile().isVerticalBarLayout()
-                            ? R.layout.qsb_container : R.layout.qsb_blocker_view,
-                    firstPage, false);
+            qsb = LayoutInflater.from(getContext())
+                    .inflate(R.layout.search_container_workspace,firstPage, false);
         }
 
         CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
         lp.canReorder = false;
-        if (!firstPage.addViewToCellLayout(qsb, 0, getEmbeddedQsbId(), lp, true)) {
+        if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
         }
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        // Update the QSB to match the cell height. This is treating the QSB essentially as a child
-        // of workspace despite that it's not a true child.
-        // Note that it relies on the strict ordering of measuring the workspace before the QSB
-        // at the dragLayer level.
-        // Only measure the QSB when the view is enabled
-        if (FeatureFlags.QSB_ON_FIRST_SCREEN && getChildCount() > 0) {
-            CellLayout firstPage = (CellLayout) getChildAt(0);
-            int cellHeight = firstPage.getCellHeight();
-
-            View qsbContainer = mLauncher.getQsbContainer();
-            ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
-            if (cellHeight > 0 && lp.height != cellHeight) {
-                lp.height = cellHeight;
-                qsbContainer.setLayoutParams(lp);
-            }
-        }
-    }
-
     public void removeAllWorkspaceScreens() {
         // Disable all layout transitions before removing all pages to ensure that we don't get the
         // transition animations competing with us changing the scroll when we add pages or the
@@ -671,7 +626,7 @@
         }
 
         // Recycle the QSB widget
-        View qsb = findViewById(getEmbeddedQsbId());
+        View qsb = findViewById(R.id.search_container_workspace);
         if (qsb != null) {
             ((ViewGroup) qsb.getParent()).removeView(qsb);
         }
@@ -709,11 +664,16 @@
 
         // Inflate the cell layout, but do not add it automatically so that we can get the newly
         // created CellLayout.
-        CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
+        CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
         newScreen.setOnLongClickListener(mLongClickListener);
         newScreen.setOnClickListener(mLauncher);
         newScreen.setSoundEffectsEnabled(false);
+
+        int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
+        int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
+        newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+
         mWorkspaceScreens.put(screenId, newScreen);
         mScreenOrder.add(insertIndex, screenId);
         addView(newScreen, insertIndex);
@@ -727,7 +687,7 @@
 
     public void createCustomContentContainer() {
         CellLayout customScreen = (CellLayout)
-                mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
+                LayoutInflater.from(getContext()).inflate(R.layout.workspace_screen, this, false);
         customScreen.disableDragTarget();
         customScreen.disableJailContent();
 
@@ -854,7 +814,7 @@
             mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
 
             // Update the model if we have changed any screens
-            mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+            LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
         }
     }
 
@@ -956,7 +916,6 @@
             return -1;
         }
 
-        int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
         CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
         mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
         mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -968,7 +927,7 @@
         mScreenOrder.add(newId);
 
         // Update the model for the new screen
-        mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         return newId;
     }
@@ -1013,7 +972,7 @@
         }
 
         int currentPage = getNextPage();
-        ArrayList<Long> removeScreens = new ArrayList<Long>();
+        ArrayList<Long> removeScreens = new ArrayList<>();
         int total = mWorkspaceScreens.size();
         for (int i = 0; i < total; i++) {
             long id = mWorkspaceScreens.keyAt(i);
@@ -1057,7 +1016,7 @@
 
         if (!removeScreens.isEmpty()) {
             // Update the model if we have changed any screens
-            mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+            LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
         }
 
         if (pageShift >= 0) {
@@ -1082,7 +1041,7 @@
 
     /**
      * Adds the specified child in the specified screen based on the {@param info}
-     * See {@link #addInScreen}.
+     * 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,
@@ -1254,7 +1213,7 @@
                         && v.getTag() instanceof LauncherAppWidgetInfo) {
                     LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
                     LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v;
-                    if (lahv.isReinflateRequired()) {
+                    if (lahv.isReinflateRequired(mLauncher.getOrientation())) {
                         // Remove and rebind the current widget (which was inflated in the wrong
                         // orientation), but don't delete it from the database
                         mLauncher.removeItem(lahv, info, false  /* deleteFromDb */);
@@ -1371,18 +1330,10 @@
         onOverlayScrollChanged(0);
     }
 
-    @Override
-    protected int getUnboundedScrollX() {
-        if (isScrollingOverlay()) {
-            return mUnboundedScrollX;
-        }
-
-        return super.getUnboundedScrollX();
-    }
 
     private boolean isScrollingOverlay() {
         return mLauncherOverlay != null &&
-                ((mIsRtl && mUnboundedScrollX > mMaxScrollX) || (!mIsRtl && mUnboundedScrollX < 0));
+                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
     }
 
     @Override
@@ -1402,22 +1353,8 @@
     }
 
     @Override
-    public void scrollTo(int x, int y) {
-        mUnboundedScrollX = x;
-        super.scrollTo(x, y);
-    }
-
-    private void onWorkspaceOverallScrollChanged() {
-        if (!mIgnoreQsbScroll) {
-            mLauncher.getQsbContainer().setTranslationX(
-                    mOverlayTranslation + mFirstPageScrollX - getScrollX());
-        }
-    }
-
-    @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
-        onWorkspaceOverallScrollChanged();
 
         // Update the page indicator progress.
         boolean isTransitioning = mIsSwitchingState
@@ -1465,12 +1402,33 @@
         }
     }
 
+    @Override
+    protected boolean shouldFlingForVelocity(int velocityX) {
+        // When the overlay is moving, the fling or settle transition is controlled by the overlay.
+        return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
+                super.shouldFlingForVelocity(velocityX);
+    }
+
     private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
 
     /**
      * The overlay scroll is being controlled locally, just update our overlay effect
      */
     public void onOverlayScrollChanged(float scroll) {
+
+        if (Float.compare(scroll, 1f) == 0) {
+            if (!mOverlayShown) {
+                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+                        Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
+            }
+            mOverlayShown = true;
+        } else if (Float.compare(scroll, 0f) == 0) {
+            if (mOverlayShown) {
+                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+                        Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+            }
+            mOverlayShown = false;
+        }
         float offset = 0f;
         float slip = 0f;
 
@@ -1491,9 +1449,6 @@
         // device I've tried, translating the launcher causes things to get quite laggy.
         setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
         setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
-        onWorkspaceOverallScrollChanged();
-
-        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_OVERLAY_SCROLL);
     }
 
     /**
@@ -1503,9 +1458,6 @@
      */
     public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
         setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
-
-        mLauncher.getQsbContainer().setTranslationY(translation);
-        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_Y_TRANSLATION);
     }
 
     /**
@@ -1587,16 +1539,13 @@
     }
 
     @Override
-    protected void getEdgeVerticalPosition(int[] pos) {
-        View child = getChildAt(getPageCount() - 1);
-        pos[0] = child.getTop();
-        pos[1] = child.getBottom();
-    }
-
-    @Override
-    protected void notifyPageSwitchListener() {
-        super.notifyPageSwitchListener();
-
+    protected void notifyPageSwitchListener(int prevPage) {
+        super.notifyPageSwitchListener(prevPage);
+        if (prevPage != mCurrentPage) {
+            int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
+            mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+                    swipeDirection, ContainerType.WORKSPACE, prevPage);
+        }
         if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
             mCustomContentShowing = true;
             if (mCustomContentCallbacks != null) {
@@ -1691,19 +1640,20 @@
     }
 
     private void updatePageAlphaValues() {
-        if (mWorkspaceFadeInAdjacentScreens &&
-                !workspaceInModalState() &&
-                !mIsSwitchingState) {
+        if (!workspaceInModalState() && !mIsSwitchingState) {
             int screenCenter = getScrollX() + getViewportWidth() / 2;
             for (int i = numCustomPages(); i < getChildCount(); i++) {
                 CellLayout child = (CellLayout) getChildAt(i);
                 if (child != null) {
                     float scrollProgress = getScrollProgress(screenCenter, child, i);
                     float alpha = 1 - Math.abs(scrollProgress);
-                    child.getShortcutsAndWidgets().setAlpha(alpha);
-
-                    if (isQsbContainerPage(i)) {
-                        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_PAGE_SCROLL);
+                    if (mWorkspaceFadeInAdjacentScreens) {
+                        child.getShortcutsAndWidgets().setAlpha(alpha);
+                    } else {
+                        // Pages that are off-screen aren't important for accessibility.
+                        child.getShortcutsAndWidgets().setImportantForAccessibility(
+                                alpha > 0 ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                                        : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
                     }
                 }
             }
@@ -1798,34 +1748,6 @@
             mWallpaperOffset.jumpToFinal();
         }
         super.onLayout(changed, left, top, right, bottom);
-        mFirstPageScrollX = getScrollForPage(0);
-        onWorkspaceOverallScrollChanged();
-
-        final LayoutTransition transition = getLayoutTransition();
-        // If the transition is running defer updating max scroll, as some empty pages could
-        // still be present, and a max scroll change could cause sudden jumps in scroll.
-        if (transition != null && transition.isRunning()) {
-            transition.addTransitionListener(new LayoutTransition.TransitionListener() {
-
-                @Override
-                public void startTransition(LayoutTransition transition, ViewGroup container,
-                                            View view, int transitionType) {
-                    mIgnoreQsbScroll = true;
-                }
-
-                @Override
-                public void endTransition(LayoutTransition transition, ViewGroup container,
-                                          View view, int transitionType) {
-                    // Wait until all transitions are complete.
-                    if (!transition.isRunning()) {
-                        mIgnoreQsbScroll = false;
-                        transition.removeTransitionListener(this);
-                        mFirstPageScrollX = getScrollForPage(0);
-                        onWorkspaceOverallScrollChanged();
-                    }
-                }
-            });
-        }
         updatePageAlphaValues();
     }
 
@@ -1978,14 +1900,21 @@
             return;
         }
 
+        ArrayList<Long> prevScreenOrder = (ArrayList<Long>) mScreenOrder.clone();
         mScreenOrder.clear();
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             CellLayout cl = ((CellLayout) getChildAt(i));
             mScreenOrder.add(getIdForScreen(cl));
         }
-        mLauncher.getUserEventDispatcher().logOverviewReorder();
-        mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+
+        for (int i = 0; i < prevScreenOrder.size(); i++) {
+            if (mScreenOrder.get(i) != prevScreenOrder.get(i)) {
+                mLauncher.getUserEventDispatcher().logOverviewReorder();
+                break;
+            }
+        }
+        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         // Re-enable auto layout transitions for page deletion.
         enableLayoutTransitions();
@@ -2064,10 +1993,6 @@
             mLauncher.notifyWidgetProvidersChanged();
         }
 
-        if (mOnStateChangeListener != null) {
-            mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
-        }
-
         onPrepareStateTransition(mState.hasMultipleVisiblePages);
 
         StateTransitionListener listener = new StateTransitionListener();
@@ -2175,27 +2100,9 @@
         }
     }
 
-    /**
-     * Returns the drawable for the given text view.
-     */
-    public static Drawable getTextViewIcon(TextView tv) {
-        final Drawable[] drawables = tv.getCompoundDrawables();
-        for (int i = 0; i < drawables.length; i++) {
-            if (drawables[i] != null) {
-                return drawables[i];
-            }
-        }
-        return null;
-    }
-
     public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
         View child = cellInfo.cell;
 
-        // Make sure the drag was started by a long press as opposed to a long click.
-        if (!child.isInTouchMode()) {
-            return;
-        }
-
         mDragInfo = cellInfo;
         child.setVisibility(INVISIBLE);
 
@@ -2316,8 +2223,8 @@
                 mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
             }
 
-            int spanX = 1;
-            int spanY = 1;
+            int spanX;
+            int spanY;
             if (mDragInfo != null) {
                 final CellLayout.CellInfo dragCellInfo = mDragInfo;
                 spanX = dragCellInfo.spanX;
@@ -2472,11 +2379,11 @@
                 // In order to keep everything continuous, we hand off the currently rendered
                 // folder background to the newly created icon. This preserves animation state.
                 fi.setFolderBackground(mFolderCreateBg);
-                mFolderCreateBg = new FolderIcon.PreviewBackground();
+                mFolderCreateBg = new PreviewBackground();
                 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
                         postAnimationRunnable);
             } else {
-                fi.prepareCreate(v);
+                fi.prepareCreateAnimation(v);
                 fi.addItem(destInfo);
                 fi.addItem(sourceInfo);
             }
@@ -2620,7 +2527,7 @@
                         CellLayout parentCell = getParentCellLayoutForView(cell);
                         if (parentCell != null) {
                             parentCell.removeView(cell);
-                        } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                        } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
                             throw new NullPointerException("mDragInfo.cell has null parent");
                         }
                         addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2953,7 +2860,7 @@
 
         ItemInfo item = d.dragInfo;
         if (item == null) {
-            if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_DOGFOOD_BUILD) {
                 throw new NullPointerException("DragObject has null info");
             }
             return;
@@ -3149,22 +3056,19 @@
     }
 
     class FolderCreationAlarmListener implements OnAlarmListener {
-        CellLayout layout;
-        int cellX;
-        int cellY;
+        final CellLayout layout;
+        final int cellX;
+        final int cellY;
 
-        FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
+        final PreviewBackground bg = new PreviewBackground();
 
         public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
             this.layout = layout;
             this.cellX = cellX;
             this.cellY = cellY;
 
-            DeviceProfile grid = mLauncher.getDeviceProfile();
             BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
-
-            bg.setup(getResources().getDisplayMetrics(), grid, null,
-                    cell.getMeasuredWidth(), cell.getPaddingTop());
+            bg.setup(mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
 
             // The full preview background should appear behind the icon
             bg.isClipping = false;
@@ -3179,10 +3083,10 @@
     }
 
     class ReorderAlarmListener implements OnAlarmListener {
-        float[] dragViewCenter;
-        int minSpanX, minSpanY, spanX, spanY;
-        DragObject dragObject;
-        View child;
+        final float[] dragViewCenter;
+        final int minSpanX, minSpanY, spanX, spanY;
+        final DragObject dragObject;
+        final View child;
 
         public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
                 int spanY, DragObject dragObject, View child) {
@@ -3274,7 +3178,7 @@
 
             boolean findNearestVacantCell = true;
             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+                mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                         mDragViewVisualCenter[1], mTargetCell);
@@ -3340,7 +3244,7 @@
                     animationStyle, finalView, true);
         } else {
             // This is for other drag/drop cases, like dragging from All Apps
-            View view = null;
+            View view;
 
             switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -3364,7 +3268,7 @@
             // First we find the cell nearest to point at which the item is
             // dropped, without any consideration to whether there is an item there.
             if (touchXY != null) {
-                mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+                mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                         mDragViewVisualCenter[1], mTargetCell);
@@ -3610,13 +3514,13 @@
                     mDragInfo.container, mDragInfo.screenId);
             if (cellLayout != null) {
                 cellLayout.onDropChild(mDragInfo.cell);
-            } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
                 throw new RuntimeException("Invalid state: cellLayout == null in "
                         + "Workspace#onDropCompleted. Please file a bug. ");
-            };
+            }
         }
         if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
-                && mDragInfo.cell != null) {
+                && mDragInfo != null && mDragInfo.cell != null) {
             mDragInfo.cell.setVisibility(VISIBLE);
         }
         mDragInfo = null;
@@ -3636,7 +3540,7 @@
         CellLayout parentCell = getParentCellLayoutForView(v);
         if (parentCell != null) {
             parentCell.removeView(v);
-        } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+        } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
             // When an app is uninstalled using the drop target, we wait until resume to remove
             // the icon. We also remove all the corresponding items from the workspace at
             // {@link Launcher#bindComponentsRemoved}. That call can come before or after
@@ -3765,7 +3669,7 @@
      * Returns a list of all the CellLayouts in the workspace.
      */
     ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
-        ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
+        ArrayList<CellLayout> layouts = new ArrayList<>();
         int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {
             layouts.add(((CellLayout) getChildAt(screen)));
@@ -3907,7 +3811,7 @@
          * @param view view for the shortcut
          * @return true if done, false to continue the map
          */
-        public boolean evaluate(ItemInfo info, View view);
+        boolean evaluate(ItemInfo info, View view);
     }
 
     /**
@@ -3949,7 +3853,7 @@
 
     void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
         int total  = shortcuts.size();
-        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(total);
+        final HashSet<ShortcutInfo> updates = new HashSet<>(total);
         final HashSet<Long> folderIds = new HashSet<>();
 
         for (int i = 0; i < total; i++) {
@@ -3965,7 +3869,7 @@
                         updates.contains(info)) {
                     ShortcutInfo si = (ShortcutInfo) info;
                     BubbleTextView shortcut = (BubbleTextView) v;
-                    Drawable oldIcon = getTextViewIcon(shortcut);
+                    Drawable oldIcon = shortcut.getIcon();
                     boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
                     shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
@@ -4088,8 +3992,9 @@
         }
     }
 
-    private void moveToScreen(int page, boolean animate) {
-        if (!workspaceInModalState()) {
+    void moveToDefaultScreen(boolean animate) {
+        int page = getDefaultPage();
+        if (!workspaceInModalState() && getNextPage() != page) {
             if (animate) {
                 snapToPage(page);
             } else {
@@ -4102,10 +4007,6 @@
         }
     }
 
-    void moveToDefaultScreen(boolean animate) {
-        moveToScreen(getDefaultPage(), animate);
-    }
-
     void moveToCustomContentScreen(boolean animate) {
         if (hasCustomContent()) {
             int ccIndex = getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID);
@@ -4182,15 +4083,15 @@
      * Used as a workaround to ensure that the AppWidgetService receives the
      * PACKAGE_ADDED broadcast before updating widgets.
      */
-    private class DeferredWidgetRefresh implements Runnable {
+    private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
         private final ArrayList<LauncherAppWidgetInfo> mInfos;
         private final LauncherAppWidgetHost mHost;
         private final Handler mHandler;
 
         private boolean mRefreshPending;
 
-        public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
-                LauncherAppWidgetHost host) {
+        DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
+            LauncherAppWidgetHost host) {
             mInfos = infos;
             mHost = host;
             mHandler = new Handler();
@@ -4225,20 +4126,11 @@
                 }
             });
         }
-    }
 
-    public interface OnStateChangeListener {
-
-        /**
-         * Called when the workspace state is changing.
-         * @param toState final state
-         * @param targetAnim animation which will be played during the transition or null.
-         */
-        void prepareStateChange(State toState, AnimatorSet targetAnim);
-    }
-
-    public static final boolean isQsbContainerPage(int pageNo) {
-        return pageNo == 0;
+        @Override
+        public void notifyWidgetProvidersChanged() {
+            run();
+        }
     }
 
     private class StateTransitionListener extends AnimatorListenerAdapter
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 482a2c9..a105a73 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -273,10 +273,8 @@
         float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
                 1.0f : 0f;
         float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded ||
-                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
-        float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
-        float finalQsbAlpha = (states.stateIsNormal ||
-                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
+                states.stateIsNormalHidden) ? 1f : 0f;
+        float finalQsbAlpha = (states.stateIsNormal || states.stateIsNormalHidden) ? 1f : 0f;
 
         float finalWorkspaceTranslationY = 0;
         if (states.stateIsOverview || states.stateIsOverviewHidden) {
@@ -313,8 +311,7 @@
             if (states.stateIsOverviewHidden) {
                 finalAlpha = 0f;
             } else if(states.stateIsNormalHidden) {
-                finalAlpha = (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                        i == mWorkspace.getNextPage()) ? 1 : 0;
+                finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0;
             } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
                 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
             } else {
@@ -323,7 +320,7 @@
 
             // If we are animating to/from the small state, then hide the side pages and fade the
             // current page in
-            if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !mWorkspace.isSwitchingState()) {
+            if (!FeatureFlags.NO_ALL_APPS_ICON && !mWorkspace.isSwitchingState()) {
                 if (states.workspaceToAllApps || states.allAppsToWorkspace) {
                     boolean isCurrentPage = (i == toPage);
                     if (states.allAppsToWorkspace && isCurrentPage) {
@@ -355,63 +352,52 @@
                 cl.setBackgroundAlpha(finalBackgroundAlpha);
                 cl.setShortcutAndWidgetAlpha(finalAlpha);
             }
-
-            if (Workspace.isQsbContainerPage(i) &&
-                    states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
-                if (animated) {
-                    Animator anim = mWorkspace.mQsbAlphaController
-                            .animateAlphaAtIndex(finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
-                    anim.setDuration(duration);
-                    anim.setInterpolator(mZoomInInterpolator);
-                    mStateAnimator.play(anim);
-                } else {
-                    mWorkspace.mQsbAlphaController.setAlphaAtIndex(
-                            finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
-                }
-            }
         }
 
         final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
 
-        Animator qsbAlphaAnimation = mWorkspace.mQsbAlphaController
-                .animateAlphaAtIndex(finalQsbAlpha, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE);
-
+        float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
         if (animated) {
+            // This is true when transitioning between:
+            // - Overview <-> Workspace
+            // - Overview <-> Widget Tray
+            if (finalOverviewPanelAlpha != overviewPanel.getAlpha()) {
+                Animator overviewPanelAlpha = ObjectAnimator.ofFloat(
+                        overviewPanel, View.ALPHA, finalOverviewPanelAlpha);
+                overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
+                        accessibilityEnabled));
+                layerViews.addView(overviewPanel);
+
+                if (states.overviewToWorkspace) {
+                    overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
+                } else if (states.workspaceToOverview) {
+                    overviewPanelAlpha.setInterpolator(null);
+                }
+
+                overviewPanelAlpha.setDuration(duration);
+                mStateAnimator.play(overviewPanelAlpha);
+            }
+
             Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace,
                     new PropertyListBuilder().scale(mNewScale)
                             .translationY(finalWorkspaceTranslationY).build())
                     .setDuration(duration);
             scale.setInterpolator(mZoomInInterpolator);
             mStateAnimator.play(scale);
-            Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha);
-
-            Animator overviewPanelAlpha = ObjectAnimator.ofFloat(
-                    overviewPanel, View.ALPHA, finalOverviewPanelAlpha);
-            overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
-                    accessibilityEnabled));
 
             // For animation optimization, we may need to provide the Launcher transition
             // with a set of views on which to force build and manage layers in certain scenarios.
-            layerViews.addView(overviewPanel);
-            layerViews.addView(mLauncher.getQsbContainer());
             layerViews.addView(mLauncher.getHotseat());
             layerViews.addView(mWorkspace.getPageIndicator());
 
+            Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha);
             if (states.workspaceToOverview) {
                 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
-                overviewPanelAlpha.setInterpolator(null);
             } else if (states.overviewToWorkspace) {
                 hotseatAlpha.setInterpolator(null);
-                overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
             }
-
-            overviewPanelAlpha.setDuration(duration);
             hotseatAlpha.setDuration(duration);
-            qsbAlphaAnimation.setDuration(duration);
-
-            mStateAnimator.play(overviewPanelAlpha);
             mStateAnimator.play(hotseatAlpha);
-            mStateAnimator.play(qsbAlphaAnimation);
             mStateAnimator.addListener(new AnimatorListenerAdapter() {
                 boolean canceled = false;
                 @Override
@@ -439,7 +425,6 @@
             AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
             mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
 
-            qsbAlphaAnimation.end();
             mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
             mWorkspace.updateCustomContentVisibility();
             mWorkspace.setScaleX(mNewScale);
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index d271f1d..9c23c19 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -17,8 +17,8 @@
 package com.android.launcher3.accessibility;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.folder.FolderPagedView;
 import com.android.launcher3.R;
+import com.android.launcher3.folder.FolderPagedView;
 
 /**
  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 70e5781..a0ad07a 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -18,7 +18,6 @@
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.FolderInfo;
@@ -27,7 +26,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetHostView;
 import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
@@ -37,6 +35,7 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.Thunk;
 
@@ -174,7 +173,7 @@
 
                         ArrayList<ItemInfo> itemList = new ArrayList<>();
                         itemList.add(info);
-                        mLauncher.bindItems(itemList, 0, itemList.size(), true);
+                        mLauncher.bindItems(itemList, true);
                     } else if (item instanceof PendingAddItemInfo) {
                         PendingAddItemInfo info = (PendingAddItemInfo) item;
                         Workspace workspace = mLauncher.getWorkspace();
@@ -206,7 +205,7 @@
                 public void run() {
                     ArrayList<ItemInfo> itemList = new ArrayList<>();
                     itemList.add(item);
-                    mLauncher.bindItems(itemList, 0, itemList.size(), true);
+                    mLauncher.bindItems(itemList, true);
                     announceConfirmation(R.string.item_moved);
                 }
             });
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index 8161219..5b7353a 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -50,8 +50,7 @@
         if ((host.getParent() instanceof DeepShortcutView)) {
             info.addAction(mActions.get(ADD_TO_WORKSPACE));
         } else if (host instanceof NotificationMainView) {
-            NotificationMainView notificationView = (NotificationMainView) host;
-            if (notificationView.canChildBeDismissed(notificationView)) {
+            if (((NotificationMainView) host).canChildBeDismissed()) {
                 info.addAction(mActions.get(DISMISS_NOTIFICATION));
             }
         }
@@ -74,7 +73,7 @@
                             screenId, coordinates[0], coordinates[1]);
                     ArrayList<ItemInfo> itemList = new ArrayList<>();
                     itemList.add(info);
-                    mLauncher.bindItems(itemList, 0, itemList.size(), true);
+                    mLauncher.bindItems(itemList, true);
                     AbstractFloatingView.closeAllOpenViews(mLauncher);
                     announceConfirmation(R.string.item_added_to_workspace);
                 }
@@ -88,8 +87,7 @@
             if (!(host instanceof NotificationMainView)) {
                 return false;
             }
-            NotificationMainView notificationView = (NotificationMainView) host;
-            notificationView.onChildDismissed(notificationView);
+            ((NotificationMainView) host).onChildDismissed();
             announceConfirmation(R.string.notification_dismissed);
             return true;
         }
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 9a23aa8..e6f120f 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -27,9 +27,9 @@
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.dragndrop.DragLayer;
 
 /**
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index c71bc31..3830a93 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -23,9 +23,12 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 
+import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 
 /**
  * This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
@@ -35,7 +38,7 @@
 public class AllAppsBackgroundDrawable extends Drawable {
 
     /**
-     * A helper class to positon and orient a drawable to be drawn.
+     * A helper class to position and orient a drawable to be drawn.
      */
     protected static class TransformedImageDrawable {
         private Drawable mImage;
@@ -48,9 +51,9 @@
          * @param gravity If one of the Gravity center values, the x and y offset will take the width
          *                and height of the image into account to center the image to the offset.
          */
-        public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
+        public TransformedImageDrawable(Context context, int resourceId, float xPct, float yPct,
                 int gravity) {
-            mImage = res.getDrawable(resourceId);
+            mImage = context.getDrawable(resourceId);
             mXPercent = xPct;
             mYPercent = yPct;
             mGravity = gravity;
@@ -97,19 +100,24 @@
 
     public AllAppsBackgroundDrawable(Context context) {
         Resources res = context.getResources();
-        mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
-                0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
-        mIcons = new TransformedImageDrawable[4];
-        mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
-                0.375f, 0, Gravity.CENTER_HORIZONTAL);
-        mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
-                0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
-        mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
-                0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
-        mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
-                0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
         mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
         mHeight = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_height);
+
+        context = new ContextThemeWrapper(context,
+                Themes.getAttrBoolean(context, R.attr.isMainColorDark)
+                        ? R.style.AllAppsEmptySearchBackground_Dark
+                        : R.style.AllAppsEmptySearchBackground);
+        mHand = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_hand,
+                0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
+        mIcons = new TransformedImageDrawable[4];
+        mIcons[0] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_1,
+                0.375f, 0, Gravity.CENTER_HORIZONTAL);
+        mIcons[1] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_2,
+                0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
+        mIcons[2] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_3,
+                0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
+        mIcons[3] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_4,
+                0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
     }
 
     /**
@@ -119,7 +127,8 @@
         int finalAlphaI = (int) (finalAlpha * 255f);
         if (getAlpha() != finalAlphaI) {
             mBackgroundAnim = cancelAnimator(mBackgroundAnim);
-            mBackgroundAnim = ObjectAnimator.ofInt(this, "alpha", finalAlphaI);
+            mBackgroundAnim = ObjectAnimator.ofInt(this, LauncherAnimUtils.DRAWABLE_ALPHA,
+                    finalAlphaI);
             mBackgroundAnim.setDuration(duration);
             mBackgroundAnim.start();
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java
index 622322b..583b49f 100644
--- a/src/com/android/launcher3/allapps/AllAppsCaretController.java
+++ b/src/com/android/launcher3/allapps/AllAppsCaretController.java
@@ -22,13 +22,14 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.pageindicators.CaretDrawable;
+import com.android.launcher3.touch.SwipeDetector;
 
 public class AllAppsCaretController {
     // Determines when the caret should flip. Should be accessed via getThreshold()
     private static final float CARET_THRESHOLD = 0.015f;
     private static final float CARET_THRESHOLD_LAND = 0.5f;
     // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend)
-    private static final float PEAK_VELOCITY = VerticalPullDetector.RELEASE_VELOCITY_PX_MS * .7f;
+    private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f;
 
     private Launcher mLauncher;
 
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 2c7d156..4eba5c6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,15 +20,9 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.InsetDrawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.LinearLayoutManager;
 import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.method.TextKeyListener;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -42,25 +36,23 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.PackageUserKey;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -68,26 +60,24 @@
  * The all apps view container.
  */
 public class AllAppsContainerView extends BaseContainerView implements DragSource,
-        View.OnLongClickListener, AllAppsSearchBarController.Callbacks, Insettable {
+        View.OnLongClickListener, Insettable {
 
     private final Launcher mLauncher;
     private final AlphabeticalAppsList mApps;
     private final AllAppsGridAdapter mAdapter;
-    private final RecyclerView.LayoutManager mLayoutManager;
+    private final LinearLayoutManager mLayoutManager;
 
     private AllAppsRecyclerView mAppsRecyclerView;
-    private AllAppsSearchBarController mSearchBarController;
-
+    private SearchUiManager mSearchUiManager;
     private View mSearchContainer;
-    private int mSearchContainerMinHeight;
-    private ExtendedEditText mSearchInput;
-    private HeaderElevationController mElevationController;
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
 
+    private SpringAnimationHandler mSpringAnimationHandler;
+
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -102,11 +92,10 @@
         mLauncher = Launcher.getLauncher(context);
         mApps = new AlphabeticalAppsList(context);
         mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
+        mSpringAnimationHandler = mAdapter.getSpringAnimationHandler();
         mApps.setAdapter(mAdapter);
         mLayoutManager = mAdapter.getLayoutManager();
         mSearchQueryBuilder = new SpannableStringBuilder();
-        mSearchContainerMinHeight
-                = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
 
         Selection.setSelection(mSearchQueryBuilder, 0);
     }
@@ -114,25 +103,21 @@
     @Override
     protected void updateBackground(
             int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-                getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
-                        paddingLeft, paddingTop, paddingRight, paddingBottom));
-                getContentView().setBackground(
-                        new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
-                                paddingLeft, paddingTop, paddingRight, paddingBottom));
-            } else {
-                getRevealView().setBackground(mBaseDrawable);
-            }
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
+                    paddingLeft, paddingTop, paddingRight, paddingBottom));
+            getContentView().setBackground(
+                    new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
+                            paddingLeft, paddingTop, paddingRight, paddingBottom));
         } else {
-            super.updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
+            getRevealView().setBackground(mBaseDrawable);
         }
     }
 
     /**
      * Sets the current set of predicted apps.
      */
-    public void setPredictedApps(List<ComponentKey> apps) {
+    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
         mApps.setPredictedApps(apps);
     }
 
@@ -144,19 +129,22 @@
     }
 
     /**
-     * Adds new apps to the list.
+     * Adds or updates existing apps in the list
      */
-    public void addApps(List<AppInfo> apps) {
-        mApps.addApps(apps);
-        mSearchBarController.refreshSearchResult();
+    public void addOrUpdateApps(List<AppInfo> apps) {
+        mApps.addOrUpdateApps(apps);
+        mSearchUiManager.refreshSearchResult();
     }
 
-    /**
-     * Updates existing apps in the list
-     */
-    public void updateApps(List<AppInfo> apps) {
-        mApps.updateApps(apps);
-        mSearchBarController.refreshSearchResult();
+    public void updatePromiseAppProgress(PromiseAppInfo app) {
+        int childCount = mAppsRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = mAppsRecyclerView.getChildAt(i);
+            if (child instanceof BubbleTextView && child.getTag() == app) {
+                BubbleTextView bubbleTextView = (BubbleTextView) child;
+                bubbleTextView.applyProgressLevel(app.level);
+            }
+        }
     }
 
     /**
@@ -164,53 +152,26 @@
      */
     public void removeApps(List<AppInfo> apps) {
         mApps.removeApps(apps);
-        mSearchBarController.refreshSearchResult();
-    }
-
-    public void setSearchBarVisible(boolean visible) {
-        if (visible) {
-            mSearchBarController.setVisibility(View.VISIBLE);
-        } else {
-            mSearchBarController.setVisibility(View.INVISIBLE);
-        }
-    }
-
-    /**
-     * Sets the search bar that shows above the a-z list.
-     */
-    public void setSearchBarController(AllAppsSearchBarController searchController) {
-        if (mSearchBarController != null) {
-            throw new RuntimeException("Expected search bar controller to only be set once");
-        }
-        mSearchBarController = searchController;
-        mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
-        mAdapter.setSearchController(mSearchBarController);
-    }
-
-    /**
-     * Scrolls this list view to the top.
-     */
-    public void scrollToTop() {
-        mAppsRecyclerView.scrollToTop();
+        mSearchUiManager.refreshSearchResult();
     }
 
     /**
      * Returns whether the view itself will handle the touch event or not.
      */
     public boolean shouldContainerScroll(MotionEvent ev) {
-        int[] point = new int[2];
-        point[0] = (int) ev.getX();
-        point[1] = (int) ev.getY();
-        Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point);
-
         // IF the MotionEvent is inside the search box, and the container keeps on receiving
         // touch input, container should move down.
         if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
             return true;
         }
 
+        int[] point = new int[2];
+        point[0] = (int) ev.getX();
+        point[1] = (int) ev.getY();
+        Utilities.mapCoordInSelfToDescendant(
+                mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point);
         // IF the MotionEvent is inside the thumb, container should not be pulled down.
-        if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
+        if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) {
             return false;
         }
 
@@ -223,22 +184,12 @@
     }
 
     /**
-     * Focuses the search field and begins an app search.
-     */
-    public void startAppsSearch() {
-        if (mSearchBarController != null) {
-            mSearchBarController.focusSearchField();
-        }
-    }
-
-    /**
      * Resets the state of AllApps.
      */
     public void reset() {
         // Reset the search bar and base recycler view after transitioning home
-        scrollToTop();
-        mSearchBarController.reset();
-        mAppsRecyclerView.reset();
+        mAppsRecyclerView.scrollToTop();
+        mSearchUiManager.reset();
     }
 
     @Override
@@ -256,39 +207,35 @@
             }
         });
 
-        mSearchContainer = findViewById(R.id.search_container);
-        mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
-
-        // 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("  " + mSearchInput.getHint());
-        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
-                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
-        mSearchInput.setHint(spanned);
-
-        mElevationController = new HeaderElevationController(mSearchContainer);
-
         // Load the all apps recycler view
-        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
+        mAppsRecyclerView = findViewById(R.id.apps_list_view);
         mAppsRecyclerView.setApps(mApps);
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
-        mAppsRecyclerView.addOnScrollListener(mElevationController);
-        mAppsRecyclerView.setElevationController(mElevationController);
+        // No animations will occur when changes occur to the items in this RecyclerView.
+        mAppsRecyclerView.setItemAnimator(null);
+        if (FeatureFlags.LAUNCHER3_PHYSICS) {
+            mAppsRecyclerView.setSpringAnimationHandler(mSpringAnimationHandler);
+        }
+
+        mSearchContainer = findViewById(R.id.search_container_all_apps);
+        mSearchUiManager = (SearchUiManager) mSearchContainer;
+        mSearchUiManager.initialize(mApps, mAppsRecyclerView);
+
 
         FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
         mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
         mAppsRecyclerView.preMeasureViews(mAdapter);
         mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            getRevealView().setVisibility(View.VISIBLE);
-            getContentView().setVisibility(View.VISIBLE);
-            getContentView().setBackground(null);
-        }
+        getRevealView().setVisibility(View.VISIBLE);
+        getContentView().setVisibility(View.VISIBLE);
+        getContentView().setBackground(null);
+    }
+
+    public SearchUiManager getSearchUiManager() {
+        return mSearchUiManager;
     }
 
     @Override
@@ -297,79 +244,32 @@
     }
 
     @Override
-    public void onBoundsChanged(Rect newBounds) { }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        grid.updateAppsViewNumCols();
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            if (mNumAppsPerRow != grid.inv.numColumns ||
-                    mNumPredictedAppsPerRow != grid.inv.numColumns) {
-                mNumAppsPerRow = grid.inv.numColumns;
-                mNumPredictedAppsPerRow = grid.inv.numColumns;
-
-                mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
-                mAdapter.setNumAppsPerRow(mNumAppsPerRow);
-                mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
-            }
-            if (!grid.isVerticalBarLayout()) {
-                MarginLayoutParams searchContainerLp =
-                        (MarginLayoutParams) mSearchContainer.getLayoutParams();
-
-                searchContainerLp.height = mLauncher.getDragLayer().getInsets().top
-                        + mSearchContainerMinHeight;
-                mSearchContainer.setLayoutParams(searchContainerLp);
-            }
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            return;
-        }
-
-        // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
-
         // Update the number of items in the grid before we measure the view
         grid.updateAppsViewNumCols();
-        if (mNumAppsPerRow != grid.allAppsNumCols ||
-                mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
-            mNumAppsPerRow = grid.allAppsNumCols;
-            mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
+
+        if (mNumAppsPerRow != grid.inv.numColumns ||
+                mNumPredictedAppsPerRow != grid.inv.numColumns) {
+            mNumAppsPerRow = grid.inv.numColumns;
+            mNumPredictedAppsPerRow = grid.inv.numColumns;
 
             mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
             mAdapter.setNumAppsPerRow(mNumAppsPerRow);
             mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
         }
-
-        // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        // Determine if the key event was actual text, if so, focus the search bar and then dispatch
-        // the key normally so that it can process this key event
-        if (!mSearchBarController.isSearchFieldFocused() &&
-                event.getAction() == KeyEvent.ACTION_DOWN) {
-            final int unicodeChar = event.getUnicodeChar();
-            final boolean isKeyNotWhitespace = unicodeChar > 0 &&
-                    !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
-            if (isKeyNotWhitespace) {
-                boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
-                        event.getKeyCode(), event);
-                if (gotKey && mSearchQueryBuilder.length() > 0) {
-                    mSearchBarController.focusSearchField();
-                }
-            }
-        }
-
+        mSearchUiManager.preDispatchKeyEvent(event);
         return super.dispatchKeyEvent(event);
     }
 
     @Override
     public boolean onLongClick(final View v) {
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return false;
         // When we have exited all apps or are in transition, disregard long clicks
-
         if (!mLauncher.isAppsViewVisible() ||
                 mLauncher.getWorkspace().isSwitchingState()) return false;
         // Return if global dragging is not enabled or we are already dragging
@@ -428,47 +328,17 @@
     }
 
     @Override
-    public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
-        if (apps != null) {
-            mApps.setOrderedFilter(apps);
-            mAppsRecyclerView.onSearchResultsChanged();
-            mAdapter.setLastSearchQuery(query);
-        }
-    }
-
-    @Override
-    public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
-            @NonNull AppDiscoveryUpdateState state) {
-        if (!mLauncher.isDestroyed()) {
-            mApps.onAppDiscoverySearchUpdate(app, state);
-            mAppsRecyclerView.onSearchResultsChanged();
-        }
-    }
-
-    @Override
-    public void clearSearchResult() {
-        if (mApps.setOrderedFilter(null)) {
-            mAppsRecyclerView.onSearchResultsChanged();
-        }
-
-        // Clear the search query
-        mSearchQueryBuilder.clear();
-        mSearchQueryBuilder.clearSpans();
-        Selection.setSelection(mSearchQueryBuilder, 0);
-    }
-
-    @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
-        targetParent.containerType = mAppsRecyclerView.getContainerType(v);
-    }
-
-    public boolean shouldRestoreImeState() {
-        return !TextUtils.isEmpty(mSearchInput.getText());
+        // This is filled in {@link AllAppsRecyclerView}
     }
 
     @Override
     public void setInsets(Rect insets) {
         DeviceProfile grid = mLauncher.getDeviceProfile();
+        mAppsRecyclerView.setPadding(
+                mAppsRecyclerView.getPaddingLeft(), mAppsRecyclerView.getPaddingTop(),
+                mAppsRecyclerView.getPaddingRight(), insets.bottom);
+
         if (grid.isVerticalBarLayout()) {
             ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
             mlp.leftMargin = insets.left;
@@ -480,7 +350,6 @@
             ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
             navBarBgLp.height = insets.bottom;
             navBarBg.setLayoutParams(navBarBgLp);
-            navBarBg.setVisibility(View.VISIBLE);
         }
     }
 
@@ -498,4 +367,8 @@
             }
         }
     }
+
+    public SpringAnimationHandler getSpringAnimationHandler() {
+        return mSpringAnimationHandler;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index a1ff822..e08cb15 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -16,11 +16,7 @@
 package com.android.launcher3.allapps;
 
 import android.support.v7.widget.RecyclerView;
-import android.view.View;
 
-import com.android.launcher3.BaseRecyclerViewFastScrollBar;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashSet;
@@ -210,7 +206,9 @@
         for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
             int pos = viewHolder.getAdapterPosition();
             boolean isActive = false;
-            if (mCurrentFastScrollSection != null && pos > -1) {
+            if (mCurrentFastScrollSection != null
+                    && pos > RecyclerView.NO_POSITION
+                    && pos < mApps.getAdapterItems().size()) {
                 AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
                 isActive = item != null &&
                         mCurrentFastScrollSection.equals(item.sectionName) &&
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 59cac8d..1f60fcc 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,7 +18,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Point;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -33,13 +34,17 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
-import com.android.launcher3.discovery.AppDiscoveryAppInfo;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryAppInfo;
 import com.android.launcher3.discovery.AppDiscoveryItemView;
+import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.List;
 
@@ -64,21 +69,20 @@
 
     // A divider that separates the apps list and the search market button
     public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5;
-    // The divider under the search field
-    public static final int VIEW_TYPE_SEARCH_DIVIDER = 1 << 6;
     // The divider that separates prediction icons from the app list
-    public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 7;
-    public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 8;
-    public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 9;
+    public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
+    public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 7;
+    public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 8;
 
     // Common view type masks
-    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER
-            | VIEW_TYPE_SEARCH_MARKET_DIVIDER
+    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_MARKET_DIVIDER
             | VIEW_TYPE_PREDICTION_DIVIDER;
     public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
             | VIEW_TYPE_PREDICTION_ICON;
     public static final int VIEW_TYPE_MASK_CONTENT = VIEW_TYPE_MASK_ICON
             | VIEW_TYPE_DISCOVERY_ITEM;
+    public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
+            | VIEW_TYPE_PREDICTION_DIVIDER;
 
 
     public interface BindViewCallback {
@@ -89,6 +93,7 @@
      * ViewHolder for each icon.
      */
     public static class ViewHolder extends RecyclerView.ViewHolder {
+
         public ViewHolder(View v) {
             super(v);
         }
@@ -160,11 +165,6 @@
             }
             return extraRows;
         }
-
-        @Override
-        public int getPaddingBottom() {
-            return mLauncher.getDragLayer().getInsets().bottom;
-        }
     }
 
     /**
@@ -199,7 +199,6 @@
     private int mAppsPerRow;
 
     private BindViewCallback mBindViewCallback;
-    private AllAppsSearchBarController mSearchController;
     private OnFocusChangeListener mIconFocusListener;
 
     // The text to show when there are no search results and no market search handler.
@@ -207,6 +206,8 @@
     // The intent to send off to the market app, updated each time the search query changes.
     private Intent mMarketSearchIntent;
 
+    private SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
+
     public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
             iconClickListener, View.OnLongClickListener iconLongClickListener) {
         Resources res = launcher.getResources();
@@ -219,6 +220,14 @@
         mLayoutInflater = LayoutInflater.from(launcher);
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
+        if (FeatureFlags.LAUNCHER3_PHYSICS) {
+            mSpringAnimationHandler = new SpringAnimationHandler<>(
+                    SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
+        }
+    }
+
+    public SpringAnimationHandler getSpringAnimationHandler() {
+        return mSpringAnimationHandler;
     }
 
     public static boolean isDividerViewType(int viewType) {
@@ -241,8 +250,8 @@
         mGridLayoutMgr.setSpanCount(appsPerRow);
     }
 
-    public void setSearchController(AllAppsSearchBarController searchController) {
-        mSearchController = searchController;
+    public int getNumAppsPerRow() {
+        return mAppsPerRow;
     }
 
     public void setIconFocusListener(OnFocusChangeListener focusListener) {
@@ -256,7 +265,7 @@
     public void setLastSearchQuery(String query) {
         Resources res = mLauncher.getResources();
         mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
-        mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
+        mMarketSearchIntent = PackageManagerHelper.getMarketSearchIntent(mLauncher, query);
     }
 
     /**
@@ -282,12 +291,11 @@
                         R.layout.all_apps_icon, parent, false);
                 icon.setOnClickListener(mIconClickListener);
                 icon.setOnLongClickListener(mIconLongClickListener);
-                icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
-                        .getLongPressTimeout());
+                icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
                 icon.setOnFocusChangeListener(mIconFocusListener);
 
-                // Ensure the all apps icon height matches the workspace icons
-                icon.getLayoutParams().height = getCellSize().y;
+                // Ensure the all apps icon height matches the workspace icons in portrait mode.
+                icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
                 return new ViewHolder(icon);
             case VIEW_TYPE_DISCOVERY_ITEM:
                 AppDiscoveryItemView appDiscoveryItemView = (AppDiscoveryItemView) mLayoutInflater
@@ -308,9 +316,6 @@
                     }
                 });
                 return new ViewHolder(searchMarketView);
-            case VIEW_TYPE_SEARCH_DIVIDER:
-                return new ViewHolder(mLayoutInflater.inflate(
-                        R.layout.all_apps_search_divider, parent, false));
             case VIEW_TYPE_APPS_LOADING_DIVIDER:
                 View loadingDividerView = mLayoutInflater.inflate(
                         R.layout.all_apps_discovery_loading_divider, parent, false);
@@ -324,10 +329,6 @@
         }
     }
 
-    private Point getCellSize() {
-        return mLauncher.getDeviceProfile().getCellSize();
-    }
-
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
@@ -336,7 +337,6 @@
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.applyFromApplicationInfo(info);
-                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
             case VIEW_TYPE_DISCOVERY_ITEM:
                 AppDiscoveryAppInfo appDiscoveryAppInfo = (AppDiscoveryAppInfo)
@@ -374,6 +374,22 @@
     }
 
     @Override
+    public void onViewAttachedToWindow(ViewHolder holder) {
+        int type = holder.getItemViewType();
+        if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+            mSpringAnimationHandler.add(holder.itemView, holder);
+        }
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(ViewHolder holder) {
+        int type = holder.getItemViewType();
+        if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+            mSpringAnimationHandler.remove(holder.itemView);
+        }
+    }
+
+    @Override
     public boolean onFailedToRecycleView(ViewHolder holder) {
         // Always recycle and we will reset the view when it is bound
         return true;
@@ -389,4 +405,130 @@
         AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
         return item.viewType;
     }
+
+    /**
+     * Helper class to set the SpringAnimation values for an item in the adapter.
+     */
+    private class AllAppsSpringAnimationFactory
+            implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
+        private static final float DEFAULT_MAX_VALUE_PX = 100;
+        private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
+
+        // Damping ratio range is [0, 1]
+        private static final float SPRING_DAMPING_RATIO = 0.55f;
+
+        // Stiffness is a non-negative number.
+        private static final float MIN_SPRING_STIFFNESS = 580f;
+        private static final float MAX_SPRING_STIFFNESS = 900f;
+
+        // The amount by which each adjacent rows' stiffness will differ.
+        private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
+
+        @Override
+        public SpringAnimation initialize(ViewHolder vh) {
+            return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
+        }
+
+        /**
+         * @param spring A new or recycled SpringAnimation.
+         * @param vh The ViewHolder that {@param spring} is related to.
+         */
+        @Override
+        public void update(SpringAnimation spring, ViewHolder vh) {
+            int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
+            int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
+                    mAppsPerRow);
+
+            int col = appPosition % mAppsPerRow;
+            int row = appPosition / mAppsPerRow;
+
+            int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
+            if (row > (numTotalRows / 2)) {
+                // Mirror the rows so that the top row acts the same as the bottom row.
+                row = Math.abs(numTotalRows - row);
+            }
+
+            calculateSpringValues(spring, row, col);
+        }
+
+        @Override
+        public void setDefaultValues(SpringAnimation spring) {
+            calculateSpringValues(spring, 0, mAppsPerRow / 2);
+        }
+
+        /**
+         * We manipulate the stiffness, min, and max values based on the items distance to the
+         * first row and the items distance to the center column to create the ^-shaped motion
+         * effect.
+         */
+        private void calculateSpringValues(SpringAnimation spring, int row, int col) {
+            float rowFactor = (1 + row) * 0.5f;
+            float colFactor = getColumnFactor(col, mAppsPerRow);
+
+            float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
+            float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
+
+            float stiffness = Utilities.boundToRange(
+                    MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
+                    MIN_SPRING_STIFFNESS,
+                    MAX_SPRING_STIFFNESS);
+
+            spring.setMinValue(minValue)
+                    .setMaxValue(maxValue)
+                    .getSpring()
+                    .setStiffness(stiffness)
+                    .setDampingRatio(SPRING_DAMPING_RATIO);
+        }
+
+        /**
+         * @return The app position is the position of the app in the Adapter if we ignored all
+         * other view types.
+         *
+         * The first app is at position 0, and the first app each following row is at a
+         * position that is a multiple of {@param appsPerRow}.
+         *
+         * ie. If there are 5 apps per row, and there are two rows of apps:
+         *     0 1 2 3 4
+         *     5 6 7 8 9
+         */
+        private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
+            if (position < numPredictedApps) {
+                // Predicted apps are first in the adapter.
+                return position;
+            }
+
+            // There is at most 1 divider view between the predicted apps and the alphabetical apps.
+            int numDividerViews = numPredictedApps == 0 ? 0 : 1;
+
+            // This offset takes into consideration an incomplete row of predicted apps.
+            int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+            return position + numPredictedAppsOffset - numDividerViews;
+        }
+
+        /**
+         * Increase the column factor as the distance increases between the column and the center
+         * column(s).
+         */
+        private float getColumnFactor(int col, int numCols) {
+            float centerColumn = numCols / 2;
+            int distanceToCenter = (int) Math.abs(col - centerColumn);
+
+            boolean evenNumberOfColumns = numCols % 2 == 0;
+            if (evenNumberOfColumns && col < centerColumn) {
+                distanceToCenter -= 1;
+            }
+
+            float factor = 0;
+            while (distanceToCenter > 0) {
+                if (distanceToCenter == 1) {
+                    factor += 0.2f;
+                } else {
+                    factor += 0.1f;
+                }
+                --distanceToCenter;
+            }
+
+            return factor;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 64e2fcb..494cd5a 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,12 +15,14 @@
  */
 package com.android.launcher3.allapps;
 
+import android.animation.ObjectAnimator;
 import android.content.Context;
 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.Property;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -28,18 +30,23 @@
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.SpringAnimationHandler;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 import java.util.List;
 
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
-public class AllAppsRecyclerView extends BaseRecyclerView {
+public class AllAppsRecyclerView extends BaseRecyclerView implements LogContainerProvider {
 
     private AlphabeticalAppsList mApps;
     private AllAppsFastScrollHelper mFastScrollHelper;
@@ -53,7 +60,23 @@
     private AllAppsBackgroundDrawable mEmptySearchBackground;
     private int mEmptySearchBackgroundTopOffset;
 
-    private HeaderElevationController mElevationController;
+    private SpringAnimationHandler mSpringAnimationHandler;
+    private OverScrollHelper mOverScrollHelper;
+    private SwipeDetector mPullDetector;
+
+    private float mContentTranslationY = 0;
+    public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
+            new Property<AllAppsRecyclerView, Float>(Float.class, "appsRecyclerViewContentTransY") {
+                @Override
+                public Float get(AllAppsRecyclerView allAppsRecyclerView) {
+                    return allAppsRecyclerView.getContentTranslationY();
+                }
+
+                @Override
+                public void set(AllAppsRecyclerView allAppsRecyclerView, Float y) {
+                    allAppsRecyclerView.setContentTranslationY(y);
+                }
+            };
 
     public AllAppsRecyclerView(Context context) {
         this(context, null);
@@ -72,9 +95,28 @@
         super(context, attrs, defStyleAttr);
         Resources res = getResources();
         addOnItemTouchListener(this);
-        mScrollbar.setDetachThumbOnFastScroll();
         mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
                 R.dimen.all_apps_empty_search_bg_top_offset);
+
+        mOverScrollHelper = new OverScrollHelper();
+        mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
+        mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
+    }
+
+    public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
+        if (FeatureFlags.LAUNCHER3_PHYSICS) {
+            mSpringAnimationHandler = springAnimationHandler;
+            addOnScrollListener(new SpringMotionOnScrollListener());
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        mPullDetector.onTouchEvent(e);
+        if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
+            mSpringAnimationHandler.addMovement(e);
+        }
+        return super.onTouchEvent(e);
     }
 
     /**
@@ -85,8 +127,8 @@
         mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
     }
 
-    public void setElevationController(HeaderElevationController elevationController) {
-        mElevationController = elevationController;
+    public AlphabeticalAppsList getApps() {
+        return mApps;
     }
 
     /**
@@ -98,7 +140,6 @@
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
@@ -125,8 +166,6 @@
                 AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
                 AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER);
         putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
-                AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER);
-        putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
                 AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
         putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
                 AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH);
@@ -152,13 +191,10 @@
      */
     public void scrollToTop() {
         // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
-        if (mScrollbar.isThumbDetached()) {
+        if (mScrollbar != null) {
             mScrollbar.reattachThumbToScroll();
         }
         scrollToPosition(0);
-        if (mElevationController != null) {
-            mElevationController.reset();
-        }
     }
 
     @Override
@@ -172,6 +208,26 @@
     }
 
     @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.translate(0, mContentTranslationY);
+        super.dispatchDraw(canvas);
+        canvas.translate(0, -mContentTranslationY);
+    }
+
+    public float getContentTranslationY() {
+        return mContentTranslationY;
+    }
+
+    /**
+     * Use this method instead of calling {@link #setTranslationY(float)}} directly to avoid drawing
+     * on top of other Views.
+     */
+    public void setContentTranslationY(float y) {
+        mContentTranslationY = y;
+        invalidate();
+    }
+
+    @Override
     protected boolean verifyDrawable(Drawable who) {
         return who == mEmptySearchBackground || super.verifyDrawable(who);
     }
@@ -181,9 +237,10 @@
         updateEmptySearchBackgroundBounds();
     }
 
-    public int getContainerType(View v) {
+    @Override
+    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         if (mApps.hasFilter()) {
-            return ContainerType.SEARCHRESULT;
+            targetParent.containerType = ContainerType.SEARCHRESULT;
         } else {
             if (v instanceof BubbleTextView) {
                 BubbleTextView icon = (BubbleTextView) v;
@@ -192,11 +249,13 @@
                     List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
                     AlphabeticalAppsList.AdapterItem item = items.get(position);
                     if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
-                        return ContainerType.PREDICTION;
+                        targetParent.containerType = ContainerType.PREDICTION;
+                        target.predictedRank = item.rowAppIndex;
+                        return;
                     }
                 }
             }
-            return ContainerType.ALLAPPS;
+            targetParent.containerType = ContainerType.ALLAPPS;
         }
     }
 
@@ -222,7 +281,8 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent e) {
-        boolean result = super.onInterceptTouchEvent(e);
+        mPullDetector.onTouchEvent(e);
+        boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll();
         if (!result && e.getAction() == MotionEvent.ACTION_DOWN
                 && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.setHotspot(e.getX(), e.getY());
@@ -279,6 +339,22 @@
         mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter);
     }
 
+    @Override
+    protected float getBottomFadingEdgeStrength() {
+        // No bottom fading edge.
+        return 0;
+    }
+
+    @Override
+    protected boolean isPaddingOffsetRequired() {
+        return true;
+    }
+
+    @Override
+    protected int getTopPaddingOffset() {
+        return -getPaddingTop();
+    }
+
     /**
      * Updates the bounds for the scrollbar.
      */
@@ -349,7 +425,7 @@
     }
 
     @Override
-    protected boolean supportsFastScrolling() {
+    public boolean supportsFastScrolling() {
         // Only allow fast scrolling when the user is not searching, since the results are not
         // grouped in a meaningful order
         return !mApps.hasFilter();
@@ -369,7 +445,8 @@
         if (position == NO_POSITION) {
             return -1;
         }
-        return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
+        return getPaddingTop() +
+                getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
     }
 
     public int getCurrentScrollY(int position, int offset) {
@@ -399,14 +476,7 @@
             }
             mCachedScrollPositions.put(position, y);
         }
-
-        return getPaddingTop() + y - offset;
-    }
-
-    @Override
-    protected int getScrollbarTrackHeight() {
-        return super.getScrollbarTrackHeight()
-                - Launcher.getLauncher(getContext()).getDragLayer().getInsets().bottom;
+        return y - offset;
     }
 
     /**
@@ -415,9 +485,8 @@
      */
     @Override
     protected int getAvailableScrollHeight() {
-        int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0);
-        int totalHeight = paddedHeight + getPaddingBottom();
-        return totalHeight - getScrollbarTrackHeight();
+        return getPaddingTop() + getCurrentScrollY(mApps.getAdapterItems().size(), 0)
+                - getHeight() + getPaddingBottom();
     }
 
     /**
@@ -436,4 +505,113 @@
                 y + mEmptySearchBackground.getIntrinsicHeight());
     }
 
+    private class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
+
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            if (mOverScrollHelper.isInOverScroll()) {
+                // OverScroll will handle animating the springs.
+                return;
+            }
+
+            // We only start the spring animation when we hit the top/bottom, to ensure
+            // that all of the animations start at the same time.
+            if (dy < 0 && !canScrollVertically(-1)) {
+                mSpringAnimationHandler.animateToFinalPosition(0, 1);
+            } else if (dy > 0 && !canScrollVertically(1)) {
+                mSpringAnimationHandler.animateToFinalPosition(0, -1);
+            }
+        }
+    }
+
+    private class OverScrollHelper implements SwipeDetector.Listener {
+
+        private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
+        private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
+
+        private boolean mIsInOverScroll;
+
+        // We use this value to calculate the actual amount the user has overscrolled.
+        private float mFirstDisplacement = 0;
+
+        private boolean mAlreadyScrollingUp;
+        private int mFirstScrollYOnScrollUp;
+
+        @Override
+        public void onDragStart(boolean start) {
+        }
+
+        @Override
+        public boolean onDrag(float displacement, float velocity) {
+            boolean isScrollingUp = displacement > 0;
+            if (isScrollingUp) {
+                if (!mAlreadyScrollingUp) {
+                    mFirstScrollYOnScrollUp = getCurrentScrollY();
+                    mAlreadyScrollingUp = true;
+                }
+            } else {
+                mAlreadyScrollingUp = false;
+            }
+
+            // Only enter overscroll if the user is interacting with the RecyclerView directly
+            // and if one of the following criteria are met:
+            // - User scrolls down when they're already at the bottom.
+            // - User starts scrolling up, hits the top, and continues scrolling up.
+            boolean wasInOverScroll = mIsInOverScroll;
+            mIsInOverScroll = !mScrollbar.isDraggingThumb() &&
+                    ((!canScrollVertically(1) && displacement < 0) ||
+                    (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0));
+
+            if (wasInOverScroll && !mIsInOverScroll) {
+                // Exit overscroll. This can happen when the user is in overscroll and then
+                // scrolls the opposite way.
+                reset(false /* shouldSpring */);
+            } else if (mIsInOverScroll) {
+                if (Float.compare(mFirstDisplacement, 0) == 0) {
+                    // Because users can scroll before entering overscroll, we need to
+                    // subtract the amount where the user was not in overscroll.
+                    mFirstDisplacement = displacement;
+                }
+                float overscrollY = displacement - mFirstDisplacement;
+                setContentTranslationY(getDampedOverScroll(overscrollY));
+            }
+
+            return mIsInOverScroll;
+        }
+
+        @Override
+        public void onDragEnd(float velocity, boolean fling) {
+           reset(mIsInOverScroll  /* shouldSpring */);
+        }
+
+        private void reset(boolean shouldSpring) {
+            float y = getContentTranslationY();
+            if (Float.compare(y, 0) != 0) {
+                if (FeatureFlags.LAUNCHER3_PHYSICS && shouldSpring) {
+                    // We calculate our own velocity to give the springs the desired effect.
+                    float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
+                    // We want to negate the velocity because we are moving to 0 from -1 due to the
+                    // downward motion. (y-axis -1 is above 0).
+                    mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
+                }
+
+                ObjectAnimator.ofFloat(AllAppsRecyclerView.this,
+                        AllAppsRecyclerView.CONTENT_TRANS_Y, 0)
+                        .setDuration(100)
+                        .start();
+            }
+            mIsInOverScroll = false;
+            mFirstDisplacement = 0;
+            mFirstScrollYOnScrollUp = 0;
+            mAlreadyScrollingUp = false;
+        }
+
+        public boolean isInOverScroll() {
+            return mIsInOverScroll;
+        }
+
+        private float getDampedOverScroll(float y) {
+            return OverScroll.dampedScroll(y, getHeight());
+        }
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 6587ad7..517dc94 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -20,7 +20,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
 import com.android.launcher3.BubbleTextView;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 30ed180..743b16e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -7,6 +7,7 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.graphics.Color;
+import android.support.animation.SpringAnimation;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.MotionEvent;
@@ -22,8 +23,13 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TouchController;
 
@@ -37,17 +43,18 @@
  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
  * closer to top or closer to the page indicator.
  */
-public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener,
-        View.OnLayoutChangeListener {
+public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener,
+         SearchUiManager.OnScrollRangeChangeListener {
 
     private static final String TAG = "AllAppsTrans";
     private static final boolean DBG = false;
 
-    private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
+    private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f);
+    private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
     private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
     private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
-    private final VerticalPullDetector.ScrollInterpolator mScrollInterpolator
-            = new VerticalPullDetector.ScrollInterpolator();
+    private final SwipeDetector.ScrollInterpolator mScrollInterpolator
+            = new SwipeDetector.ScrollInterpolator();
 
     private static final float PARALLAX_COEFFICIENT = .125f;
     private static final int SINGLE_FRAME_MS = 16;
@@ -63,8 +70,9 @@
     private float mStatusBarHeight;
 
     private final Launcher mLauncher;
-    private final VerticalPullDetector mDetector;
+    private final SwipeDetector mDetector;
     private final ArgbEvaluator mEvaluator;
+    private final boolean mIsDarkTheme;
 
     // Animation in this class is controlled by a single variable {@link mProgress}.
     // Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -87,26 +95,32 @@
 
     private AnimatorSet mCurrentAnimation;
     private boolean mNoIntercept;
+    private boolean mTouchEventStartedOnHotseat;
 
     // Used in discovery bounce animation to provide the transition without workspace changing.
     private boolean mIsTranslateWithoutWorkspace = false;
-    private AnimatorSet mDiscoBounceAnimation;
+    private Animator mDiscoBounceAnimation;
+    private GradientView mGradientView;
+
+    private SpringAnimation mSearchSpring;
+    private SpringAnimationHandler mSpringAnimationHandler;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
-        mDetector = new VerticalPullDetector(l);
-        mDetector.setListener(this);
+        mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
         mShiftRange = DEFAULT_SHIFT_RANGE;
         mProgress = 1f;
 
         mEvaluator = new ArgbEvaluator();
         mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary);
+        mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
     }
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = false;
+            mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev);
             if (!mLauncher.isAllAppsVisible() && mLauncher.getWorkspace().workspaceInModalState()) {
                 mNoIntercept = true;
             } else if (mLauncher.isAllAppsVisible() &&
@@ -122,17 +136,17 @@
 
                 if (mDetector.isIdleState()) {
                     if (mLauncher.isAllAppsVisible()) {
-                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+                        directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
                     } else {
-                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+                        directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
                     }
                 } else {
                     if (isInDisallowRecatchBottomZone()) {
-                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+                        directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE;
                     } else if (isInDisallowRecatchTopZone()) {
-                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+                        directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE;
                     } else {
-                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_BOTH;
+                        directionsToDetectScroll |= SwipeDetector.DIRECTION_BOTH;
                         ignoreSlopWhenSettling = true;
                     }
                 }
@@ -153,6 +167,9 @@
 
     @Override
     public boolean onControllerTouchEvent(MotionEvent ev) {
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.addMovement(ev);
+        }
         return mDetector.onTouchEvent(ev);
     }
 
@@ -171,6 +188,9 @@
         mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
         mShiftStart = mAppsView.getTranslationY();
         preparePull(start);
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.skipToEnd();
+        }
     }
 
     @Override
@@ -193,6 +213,9 @@
             return; // early termination.
         }
 
+        final int containerType = mTouchEventStartedOnHotseat
+                ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+
         if (fling) {
             if (velocity < 0) {
                 calculateDuration(velocity, mAppsView.getTranslationY());
@@ -201,11 +224,14 @@
                     mLauncher.getUserEventDispatcher().logActionOnContainer(
                             Action.Touch.FLING,
                             Action.Direction.UP,
-                            ContainerType.HOTSEAT);
+                            containerType);
                 }
-                mLauncher.showAppsView(true /* animated */,
-                        false /* updatePredictedApps */,
-                        false /* focusSearchBar */);
+                mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
+                if (hasSpringAnimationHandler()) {
+                    mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */);
+                    // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
+                    mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
+                }
             } else {
                 calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
                 mLauncher.showWorkspace(true);
@@ -221,11 +247,9 @@
                     mLauncher.getUserEventDispatcher().logActionOnContainer(
                             Action.Touch.SWIPE,
                             Action.Direction.UP,
-                            ContainerType.HOTSEAT);
+                            containerType);
                 }
-                mLauncher.showAppsView(true, /* animated */
-                        false /* updatePredictedApps */,
-                        false /* focusSearchBar */);
+                mLauncher.showAppsView(true, /* animated */ false /* updatePredictedApps */);
             }
         }
     }
@@ -247,20 +271,40 @@
             if (!mLauncher.isAllAppsVisible()) {
                 mLauncher.tryAndUpdatePredictedApps();
                 mAppsView.setVisibility(View.VISIBLE);
-                mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
+                if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+                    mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
+                }
             }
         }
     }
 
     private void updateLightStatusBar(float shift) {
         // Do not modify status bar on landscape as all apps is not full bleed.
-        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+        if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
+                && mLauncher.getDeviceProfile().isVerticalBarLayout()) {
             return;
         }
-        // Use a light status bar (dark icons) if all apps is behind at least half of the status
-        // bar. If the status bar is already light due to wallpaper extraction, keep it that way.
-        boolean forceLight = shift <= mStatusBarHeight / 2;
-        mLauncher.activateLightSystemBars(forceLight, true /* statusBar */, true /* navBar */);
+
+        // Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
+        boolean forceChange = FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS ?
+                shift <= mShiftRange / 4 :
+                shift <= mStatusBarHeight / 2;
+        if (forceChange) {
+            mLauncher.getSystemUiController().updateUiState(
+                    SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
+        } else {
+            mLauncher.getSystemUiController().updateUiState(
+                    SystemUiController.UI_STATE_ALL_APPS, 0);
+        }
+    }
+
+    private void updateAllAppsBg(float progress) {
+        // gradient
+        if (mGradientView == null) {
+            mGradientView = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
+            mGradientView.setVisibility(View.VISIBLE);
+        }
+        mGradientView.setProgress(progress);
     }
 
     /**
@@ -273,31 +317,37 @@
 
         float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f);
         float alpha = 1 - workspaceHotseatAlpha;
-        float interpolation = mAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
+        float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha);
+        float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
 
         int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha),
                 mHotseatBackgroundColor, mAllAppsBackgroundColor);
         int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha,
                 mHotseatBackgroundColor, mAllAppsBackgroundColor));
 
-        mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            updateAllAppsBg(alpha);
+        } else {
+            mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
+        }
+
         mAppsView.getContentView().setAlpha(alpha);
         mAppsView.setTranslationY(shiftCurrent);
 
         if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
             mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y, -mShiftRange + shiftCurrent,
-                    interpolation);
+                    hotseatAlpha);
         } else {
             mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y,
                     PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent),
-                    interpolation);
+                    hotseatAlpha);
         }
 
         if (mIsTranslateWithoutWorkspace) {
             return;
         }
         mWorkspace.setWorkspaceYTranslationAndAlpha(
-                PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), interpolation);
+                PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), workspaceAlpha);
 
         if (!mDetector.isDraggingState()) {
             mContainerVelocity = mDetector.computeVelocity(shiftCurrent - shiftPrevious,
@@ -313,7 +363,7 @@
     }
 
     private void calculateDuration(float velocity, float disp) {
-        mAnimationDuration = mDetector.calculateDuration(velocity, disp / mShiftRange);
+        mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange);
     }
 
     public boolean animateToAllApps(AnimatorSet animationOut, long duration) {
@@ -371,8 +421,8 @@
         cancelDiscoveryAnimation();
 
         // assumption is that this variable is always null
-        mDiscoBounceAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mLauncher,
-                R.anim.discovery_bounce);
+        mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
+                R.animator.discovery_bounce);
         mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animator) {
@@ -451,6 +501,10 @@
 
     public void finishPullUp() {
         mHotseat.setVisibility(View.INVISIBLE);
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.remove(mSearchSpring);
+            mSpringAnimationHandler.reset();
+        }
         setProgress(0f);
     }
 
@@ -459,6 +513,9 @@
         mHotseat.setBackgroundTransparent(false /* transparent */);
         mHotseat.setVisibility(View.VISIBLE);
         mAppsView.reset();
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.reset();
+        }
         setProgress(1f);
     }
 
@@ -486,21 +543,21 @@
         mAppsView = appsView;
         mHotseat = hotseat;
         mWorkspace = workspace;
-        mHotseat.addOnLayoutChangeListener(this);
         mHotseat.bringToFront();
         mCaretController = new AllAppsCaretController(
                 mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
+        mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
+        mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
+        mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling();
+    }
+
+    private boolean hasSpringAnimationHandler() {
+        return FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null;
     }
 
     @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-            mShiftRange = top;
-        } else {
-            mShiftRange = bottom;
-        }
+    public void onScrollRangeChanged(int scrollRange) {
+        mShiftRange = scrollRange;
         setProgress(mProgress);
     }
-
 }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index f5cf7ef..6bbe3ea 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -24,11 +24,12 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.discovery.AppDiscoveryAppInfo;
 import com.android.launcher3.discovery.AppDiscoveryItem;
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.LabelComparator;
 
 import java.util.ArrayList;
@@ -138,13 +139,6 @@
             return item;
         }
 
-        public static AdapterItem asSearchDivider(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER;
-            item.position = pos;
-            return item;
-        }
-
         public static AdapterItem asMarketDivider(int pos) {
             AdapterItem item = new AdapterItem();
             item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
@@ -180,7 +174,7 @@
     // The set of sections that we allow fast-scrolling to (includes non-merged sections)
     private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
     // The set of predicted app component names
-    private final List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
+    private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
     // The set of predicted apps resolved from the component names and the current set of apps
     private final List<AppInfo> mPredictedApps = new ArrayList<>();
     private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
@@ -226,6 +220,13 @@
     }
 
     /**
+     * Returns the predicted apps.
+     */
+    public List<AppInfo> getPredictedApps() {
+        return mPredictedApps;
+    }
+
+    /**
      * Returns fast scroller sections of all the current filtered applications.
      */
     public List<FastScrollSectionInfo> getFastScrollerSections() {
@@ -298,14 +299,75 @@
         updateAdapterItems();
     }
 
+    private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
+        if (mComponentToAppMap.isEmpty()) {
+            // Apps have not been bound yet.
+            return Collections.emptyList();
+        }
+
+        List<AppInfo> predictedApps = new ArrayList<>();
+        for (ComponentKeyMapper<AppInfo> mapper : components) {
+            AppInfo info = mapper.getItem(mComponentToAppMap);
+            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;
+    }
+
     /**
-     * Sets the current set of predicted apps.  Since this can be called before we get the full set
-     * of applications, we should merge the results only in onAppsUpdated() which is idempotent.
+     * 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 onAppsUpdated() 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<ComponentKey> apps) {
+    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
         mPredictedAppComponents.clear();
         mPredictedAppComponents.addAll(apps);
-        onAppsUpdated();
+
+        List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
+        // We only need to do work if any of the visible predicted apps have changed.
+        if (!newPredictedApps.equals(mPredictedApps)) {
+            if (newPredictedApps.size() == mPredictedApps.size()) {
+                swapInNewPredictedApps(newPredictedApps);
+            } else {
+                // We need to update the appIndex of all the items.
+                onAppsUpdated();
+            }
+        }
+    }
+
+    /**
+     * Swaps out the old predicted apps with the new predicted apps, in place. This optimization
+     * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
+     *
+     * Note: This should only be called if the # of predicted apps is the same.
+     *       This method assumes that predicted apps are the first items in the adapter.
+     */
+    private void swapInNewPredictedApps(List<AppInfo> apps) {
+        mPredictedApps.clear();
+        mPredictedApps.addAll(apps);
+
+        int size = apps.size();
+        for (int i = 0; i < size; ++i) {
+            AppInfo info = apps.get(i);
+            AdapterItem appItem = AdapterItem.asPredictedApp(i, "", info, i);
+            appItem.rowAppIndex = i;
+            mAdapterItems.set(i, appItem);
+            mFilteredApps.set(i, info);
+            mAdapter.notifyItemChanged(i);
+        }
     }
 
     /**
@@ -313,20 +375,13 @@
      */
     public void setApps(List<AppInfo> apps) {
         mComponentToAppMap.clear();
-        addApps(apps);
+        addOrUpdateApps(apps);
     }
 
     /**
-     * Adds new apps to the list.
+     * Adds or updates existing apps in the list
      */
-    public void addApps(List<AppInfo> apps) {
-        updateApps(apps);
-    }
-
-    /**
-     * Updates existing apps in the list
-     */
-    public void updateApps(List<AppInfo> apps) {
+    public void addOrUpdateApps(List<AppInfo> apps) {
         for (AppInfo app : apps) {
             mComponentToAppMap.put(app.toComponentKey(), app);
         }
@@ -418,37 +473,21 @@
 
         if (DEBUG_PREDICTIONS) {
             if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
             }
         }
 
-        // Add the search divider
-        mAdapterItems.add(AdapterItem.asSearchDivider(position++));
-
         // Process the predicted app components
         mPredictedApps.clear();
         if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
-            for (ComponentKey ck : mPredictedAppComponents) {
-                AppInfo info = mComponentToAppMap.get(ck);
-                if (info != null) {
-                    mPredictedApps.add(info);
-                } else {
-                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                        Log.e(TAG, "Predicted app not found: " + ck);
-                    }
-                }
-                // Stop at the number of predicted apps
-                if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
-                    break;
-                }
-            }
+            mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
 
             if (!mPredictedApps.isEmpty()) {
                 // Add a section for the predictions
@@ -606,6 +645,10 @@
         return result;
     }
 
+    public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
+        return mapper.getItem(mComponentToAppMap);
+    }
+
     /**
      * Returns the cached section name for the given title, recomputing and updating the cache if
      * the title has no cached section name.
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
deleted file mode 100644
index 57747e3..0000000
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ /dev/null
@@ -1,26 +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.allapps;
-
-/**
- * The default search controller.
- */
-public class DefaultAppSearchController extends AllAppsSearchBarController {
-
-    public DefaultAppSearchAlgorithm onInitializeSearch() {
-        return new DefaultAppSearchAlgorithm(mApps.getApps());
-    }
-}
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/HeaderElevationController.java
deleted file mode 100644
index b167fed..0000000
--- a/src/com/android/launcher3/allapps/HeaderElevationController.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.android.launcher3.allapps;
-
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-/**
- * Helper class for controlling the header elevation in response to RecyclerView scroll.
- */
-public class HeaderElevationController extends RecyclerView.OnScrollListener {
-
-    private final View mHeader;
-    private final float mMaxElevation;
-    private final float mScrollToElevation;
-
-    private int mCurrentY = 0;
-
-    public HeaderElevationController(View header) {
-        mHeader = header;
-        final Resources res = mHeader.getContext().getResources();
-        mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
-        mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
-
-        // We need to provide a custom outline so the shadow only appears on the bottom edge.
-        // The top, left and right edges are all extended out, and the shadow is clipped
-        // by the parent.
-        final ViewOutlineProvider vop = new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                final View parent = (View) mHeader.getParent();
-
-                final int left = parent.getLeft(); // Use the parent to account for offsets
-                final int top = view.getTop();
-                final int right = left + view.getWidth();
-                final int bottom = view.getBottom();
-
-                final int offset = Utilities.pxFromDp(mMaxElevation, res.getDisplayMetrics());
-                outline.setRect(left - offset, top - offset, right + offset, bottom);
-            }
-        };
-        mHeader.setOutlineProvider(vop);
-    }
-
-    public void reset() {
-        mCurrentY = 0;
-        onScroll(mCurrentY);
-    }
-
-    @Override
-    public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-        mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
-        onScroll(mCurrentY);
-    }
-
-    private void onScroll(int scrollY) {
-        float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
-        float newElevation = mMaxElevation * elevationPct;
-        if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
-            mHeader.setElevation(newElevation);
-        }
-    }
-
-}
diff --git a/src/com/android/launcher3/allapps/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
new file mode 100644
index 0000000..cdde657
--- /dev/null
+++ b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.views.RecyclerViewFastScroller;
+
+/**
+ * Extension of {@link RecyclerViewFastScroller} to be used in landscape layout.
+ */
+public class LandscapeFastScroller extends RecyclerViewFastScroller {
+
+    public LandscapeFastScroller(Context context) {
+        super(context);
+    }
+
+    public LandscapeFastScroller(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public LandscapeFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public boolean handleTouchEvent(MotionEvent ev) {
+        // We handle our own touch event, no need to handle recycler view touch delegates.
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        event.offsetLocation(0, -mRv.getPaddingTop());
+        if (super.handleTouchEvent(event)) {
+            getParent().requestDisallowInterceptTouchEvent(true);
+        }
+        event.offsetLocation(0, mRv.getPaddingTop());
+        return true;
+    }
+
+    @Override
+    public boolean shouldBlockIntercept(int x, int y) {
+        // If the user touched the scroll bar area, block swipe
+        return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
+    }
+}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
new file mode 100644
index 0000000..34230e0
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.support.animation.SpringAnimation;
+import android.support.annotation.NonNull;
+import android.view.KeyEvent;
+
+/**
+ * Interface for controlling the Apps search UI.
+ */
+public interface SearchUiManager {
+
+    /**
+     * Initializes the search manager.
+     */
+    void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
+
+    /**
+     * A {@link SpringAnimation} that will be used when the user flings.
+     */
+    @NonNull SpringAnimation getSpringForFling();
+
+    /**
+     * Notifies the search manager that the apps-list has changed and the search UI should be
+     * updated accordingly.
+     */
+    void refreshSearchResult();
+
+    /**
+     * Notifies the search manager to close any active search session.
+     */
+    void reset();
+
+    /**
+     * Called before dispatching a key event, in case the search manager wants to initialize
+     * some UI beforehand.
+     */
+    void preDispatchKeyEvent(KeyEvent keyEvent);
+
+    void addOnScrollRangeChangeListener(OnScrollRangeChangeListener listener);
+
+    /**
+     * Callback for listening to changes in the vertical scroll range when opening all-apps.
+     */
+    interface OnScrollRangeChangeListener {
+
+        void onScrollRangeChanged(int scrollRange);
+    }
+}
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
deleted file mode 100644
index 7800c01..0000000
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ /dev/null
@@ -1,308 +0,0 @@
-package com.android.launcher3.allapps;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-import android.view.animation.Interpolator;
-
-/**
- * One dimensional scroll gesture detector for all apps container pull up interaction.
- * Client (e.g., AllAppsTransitionController) of this class can register a listener.
- * <p/>
- * Features that this gesture detector can support.
- */
-public class VerticalPullDetector {
-
-    private static final boolean DBG = false;
-    private static final String TAG = "VerticalPullDetector";
-
-    private float mTouchSlop;
-
-    private int mScrollConditions;
-    public static final int DIRECTION_UP = 1 << 0;
-    public static final int DIRECTION_DOWN = 1 << 1;
-    public static final int DIRECTION_BOTH = DIRECTION_DOWN | DIRECTION_UP;
-
-    private static final float ANIMATION_DURATION = 1200;
-    private static final float FAST_FLING_PX_MS = 10;
-
-    /**
-     * The minimum release velocity in pixels per millisecond that triggers fling..
-     */
-    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;
-
-    enum ScrollState {
-        IDLE,
-        DRAGGING,      // onDragStart, onDrag
-        SETTLING       // onDragEnd
-    }
-
-    ;
-
-    //------------------- ScrollState transition diagram -----------------------------------
-    //
-    // IDLE ->      (mDisplacement > mTouchSlop) -> DRAGGING
-    // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
-    // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
-    // SETTLING -> (View settled) -> IDLE
-
-    private void setState(ScrollState newState) {
-        if (DBG) {
-            Log.d(TAG, "setState:" + mState + "->" + newState);
-        }
-        // onDragStart and onDragEnd is reported ONLY on state transition
-        if (newState == ScrollState.DRAGGING) {
-            initializeDragging();
-            if (mState == ScrollState.IDLE) {
-                reportDragStart(false /* recatch */);
-            } else if (mState == ScrollState.SETTLING) {
-                reportDragStart(true /* recatch */);
-            }
-        }
-        if (newState == ScrollState.SETTLING) {
-            reportDragEnd();
-        }
-
-        mState = newState;
-    }
-
-    public boolean isDraggingOrSettling() {
-        return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
-    }
-
-    /**
-     * There's no touch and there's no animation.
-     */
-    public boolean isIdleState() {
-        return mState == ScrollState.IDLE;
-    }
-
-    public boolean isSettlingState() {
-        return mState == ScrollState.SETTLING;
-    }
-
-    public boolean isDraggingState() {
-        return mState == ScrollState.DRAGGING;
-    }
-
-    private float mDownX;
-    private float mDownY;
-
-    private float mLastY;
-    private long mCurrentMillis;
-
-    private float mVelocity;
-    private float mLastDisplacement;
-    private float mDisplacementY;
-    private float mDisplacementX;
-
-    private float mSubtractDisplacement;
-    private boolean mIgnoreSlopWhenSettling;
-
-    /* Client of this gesture detector can register a callback. */
-    Listener mListener;
-
-    public void setListener(Listener l) {
-        mListener = l;
-    }
-
-    public interface Listener {
-        void onDragStart(boolean start);
-
-        boolean onDrag(float displacement, float velocity);
-
-        void onDragEnd(float velocity, boolean fling);
-    }
-
-    public VerticalPullDetector(Context context) {
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-    }
-
-    public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
-        mScrollConditions = scrollDirectionFlags;
-        mIgnoreSlopWhenSettling = ignoreSlop;
-    }
-
-    private boolean shouldScrollStart() {
-        // reject cases where the slop condition is not met.
-        if (Math.abs(mDisplacementY) < mTouchSlop) {
-            return false;
-        }
-
-        // reject cases where the angle condition is not met.
-        float deltaY = Math.abs(mDisplacementY);
-        float deltaX = Math.max(Math.abs(mDisplacementX), 1);
-        if (deltaX > deltaY) {
-            return false;
-        }
-        // Check if the client is interested in scroll in current direction.
-        if (((mScrollConditions & DIRECTION_DOWN) > 0 && mDisplacementY > 0) ||
-                ((mScrollConditions & DIRECTION_UP) > 0 && mDisplacementY < 0)) {
-            return true;
-        }
-        return false;
-    }
-
-    public boolean onTouchEvent(MotionEvent ev) {
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mDownX = ev.getX();
-                mDownY = ev.getY();
-                mLastDisplacement = 0;
-                mDisplacementY = 0;
-                mVelocity = 0;
-
-                if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
-                    setState(ScrollState.DRAGGING);
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                mDisplacementX = ev.getX() - mDownX;
-                mDisplacementY = ev.getY() - mDownY;
-                computeVelocity(ev);
-
-                // handle state and listener calls.
-                if (mState != ScrollState.DRAGGING && shouldScrollStart()) {
-                    setState(ScrollState.DRAGGING);
-                }
-                if (mState == ScrollState.DRAGGING) {
-                    reportDragging();
-                }
-                break;
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                // These are synthetic events and there is no need to update internal values.
-                if (mState == ScrollState.DRAGGING) {
-                    setState(ScrollState.SETTLING);
-                }
-                break;
-            default:
-                //TODO: add multi finger tracking by tracking active pointer.
-                break;
-        }
-        // Do house keeping.
-        mLastDisplacement = mDisplacementY;
-        mLastY = ev.getY();
-        return true;
-    }
-
-    public void finishedScrolling() {
-        setState(ScrollState.IDLE);
-    }
-
-    private boolean reportDragStart(boolean recatch) {
-        mListener.onDragStart(!recatch);
-        if (DBG) {
-            Log.d(TAG, "onDragStart recatch:" + recatch);
-        }
-        return true;
-    }
-
-    private void initializeDragging() {
-        if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
-            mSubtractDisplacement = 0;
-        }
-        if (mDisplacementY > 0) {
-            mSubtractDisplacement = mTouchSlop;
-        } else {
-            mSubtractDisplacement = -mTouchSlop;
-        }
-    }
-
-    private boolean reportDragging() {
-        float delta = mDisplacementY - mLastDisplacement;
-        if (delta != 0) {
-            if (DBG) {
-                Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
-                        mDisplacementY, mVelocity));
-            }
-
-            return mListener.onDrag(mDisplacementY - mSubtractDisplacement, mVelocity);
-        }
-        return true;
-    }
-
-    private void reportDragEnd() {
-        if (DBG) {
-            Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
-                    mDisplacementY, mVelocity));
-        }
-        mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
-
-    }
-
-    /**
-     * Computes the damped velocity using the two motion events and the previous velocity.
-     */
-    private float computeVelocity(MotionEvent to) {
-        return computeVelocity(to.getY() - mLastY, to.getEventTime());
-    }
-
-    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
-     */
-    private static float interpolate(float from, float to, float alpha) {
-        return (1.0f - alpha) * from + alpha * to;
-    }
-
-    public long calculateDuration(float velocity, float progressNeeded) {
-        // TODO: make these values constants after tuning.
-        float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
-        float travelDistance = Math.max(0.2f, progressNeeded);
-        long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
-        if (DBG) {
-            Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
-        }
-        return duration;
-    }
-
-    public static class ScrollInterpolator implements Interpolator {
-
-        boolean mSteeper;
-
-        public void setVelocityAtZero(float velocity) {
-            mSteeper = velocity > FAST_FLING_PX_MS;
-        }
-
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            float output = t * t * t;
-            if (mSteeper) {
-                output *= t * t; // Make interpolation initial slope steeper
-            }
-            return output + 1;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
similarity index 79%
rename from src/com/android/launcher3/allapps/AllAppsSearchBarController.java
rename to src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index c7ba3ab..63aa7be 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -13,12 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
 
 import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Editable;
@@ -37,22 +34,22 @@
 import com.android.launcher3.discovery.AppDiscoveryItem;
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
 
 /**
  * An interface to a search box that AllApps can command.
  */
-public abstract class AllAppsSearchBarController
+public class AllAppsSearchBarController
         implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
 
     protected Launcher mLauncher;
-    protected AlphabeticalAppsList mApps;
     protected Callbacks mCb;
     protected ExtendedEditText mInput;
     protected String mQuery;
 
-    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
+    protected SearchAlgorithm mSearchAlgorithm;
     protected InputMethodManager mInputMethodManager;
 
     public void setVisibility(int visibility) {
@@ -62,9 +59,8 @@
      * Sets the references to the apps model and the search result callback.
      */
     public final void initialize(
-            AlphabeticalAppsList apps, ExtendedEditText input,
+            SearchAlgorithm searchAlgorithm, ExtendedEditText input,
             Launcher launcher, Callbacks cb) {
-        mApps = apps;
         mCb = cb;
         mLauncher = launcher;
 
@@ -76,22 +72,9 @@
         mInputMethodManager = (InputMethodManager)
                 mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
-        mSearchAlgorithm = onInitializeSearch();
-
-        onInitialized();
+        mSearchAlgorithm = searchAlgorithm;
     }
 
-    /**
-     * You can override this method to perform custom initialization.
-     */
-    protected void onInitialized() {
-    }
-
-    /**
-     * To be implemented by subclasses. This method will get called when the controller is set.
-     */
-    protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
-
     @Override
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         // Do nothing
@@ -114,7 +97,7 @@
         }
     }
 
-    protected void refreshSearchResult() {
+    public void refreshSearchResult() {
         if (TextUtils.isEmpty(mQuery)) {
             return;
         }
@@ -135,7 +118,8 @@
         if (query.isEmpty()) {
             return false;
         }
-        return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
+        return mLauncher.startActivitySafely(v,
+                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
     }
 
     @Override
@@ -186,29 +170,11 @@
     }
 
     /**
-     * Creates a new market search intent.
-     */
-    public Intent createMarketSearchIntent(String query) {
-        Uri marketSearchUri = Uri.parse("market://search")
-                .buildUpon()
-                .appendQueryParameter("c", "apps")
-                .appendQueryParameter("q", query)
-                .build();
-        return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
-    }
-
-    /**
      * Callback for getting search results.
      */
     public interface Callbacks {
 
         /**
-         * Called when the bounds of the search bar has changed.
-         */
-        @Deprecated
-        void onBoundsChanged(Rect newBounds);
-
-        /**
          * Called when the search is complete.
          *
          * @param apps sorted list of matching components or null if in case of failure.
@@ -220,7 +186,6 @@
          */
         void clearSearchResult();
 
-
         /**
          * Called when the app discovery is providing an update of search, which can either be
          * START for starting a new discovery,
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
new file mode 100644
index 0000000..ddf6e58
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.animation.FloatValueHolder;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.SearchUiManager;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryItem;
+import com.android.launcher3.discovery.AppDiscoveryUpdateState;
+import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.util.ComponentKey;
+import java.util.ArrayList;
+
+/**
+ * Layout to contain the All-apps search UI.
+ */
+public class AppsSearchContainerLayout extends FrameLayout
+        implements SearchUiManager, AllAppsSearchBarController.Callbacks {
+
+    private final Launcher mLauncher;
+    private final int mMinHeight;
+    private final int mSearchBoxHeight;
+    private final AllAppsSearchBarController mSearchBarController;
+    private final SpannableStringBuilder mSearchQueryBuilder;
+
+    private ExtendedEditText mSearchInput;
+    private AlphabeticalAppsList mApps;
+    private AllAppsRecyclerView mAppsRecyclerView;
+    private AllAppsGridAdapter mAdapter;
+    private View mDivider;
+    private HeaderElevationController mElevationController;
+
+    private SpringAnimation mSpring;
+
+    public AppsSearchContainerLayout(Context context) {
+        this(context, null);
+    }
+
+    public AppsSearchContainerLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mLauncher = Launcher.getLauncher(context);
+        mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
+        mSearchBoxHeight = getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
+        mSearchBarController = new AllAppsSearchBarController();
+
+        mSearchQueryBuilder = new SpannableStringBuilder();
+        Selection.setSelection(mSearchQueryBuilder, 0);
+
+        // Note: This spring does nothing.
+        mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSearchInput = findViewById(R.id.search_box_input);
+        mDivider = findViewById(R.id.search_divider);
+        mElevationController = new HeaderElevationController(mDivider);
+
+        // 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("  " + mSearchInput.getHint());
+        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
+                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+        mSearchInput.setHint(spanned);
+
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        if (!dp.isVerticalBarLayout()) {
+            LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
+            lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+
+    @Override
+    public void initialize(
+            AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView) {
+        mApps = appsList;
+        mAppsRecyclerView = recyclerView;
+        mAppsRecyclerView.addOnScrollListener(mElevationController);
+        mAdapter = (AllAppsGridAdapter) mAppsRecyclerView.getAdapter();
+        mSearchBarController.initialize(
+                new DefaultAppSearchAlgorithm(appsList.getApps()), mSearchInput, mLauncher, this);
+    }
+
+    @Override
+    public @NonNull SpringAnimation getSpringForFling() {
+        return mSpring;
+    }
+
+    @Override
+    public void refreshSearchResult() {
+        mSearchBarController.refreshSearchResult();
+    }
+
+    @Override
+    public void reset() {
+        mElevationController.reset();
+        mSearchBarController.reset();
+    }
+
+    @Override
+    public void preDispatchKeyEvent(KeyEvent event) {
+        // Determine if the key event was actual text, if so, focus the search bar and then dispatch
+        // the key normally so that it can process this key event
+        if (!mSearchBarController.isSearchFieldFocused() &&
+                event.getAction() == KeyEvent.ACTION_DOWN) {
+            final int unicodeChar = event.getUnicodeChar();
+            final boolean isKeyNotWhitespace = unicodeChar > 0 &&
+                    !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
+            if (isKeyNotWhitespace) {
+                boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
+                        event.getKeyCode(), event);
+                if (gotKey && mSearchQueryBuilder.length() > 0) {
+                    mSearchBarController.focusSearchField();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
+        if (apps != null) {
+            mApps.setOrderedFilter(apps);
+            notifyResultChanged();
+            mAdapter.setLastSearchQuery(query);
+        }
+    }
+
+    @Override
+    public void clearSearchResult() {
+        if (mApps.setOrderedFilter(null)) {
+            notifyResultChanged();
+        }
+
+        // Clear the search query
+        mSearchQueryBuilder.clear();
+        mSearchQueryBuilder.clearSpans();
+        Selection.setSelection(mSearchQueryBuilder, 0);
+    }
+
+    @Override
+    public void onAppDiscoverySearchUpdate(
+            @Nullable AppDiscoveryItem app, @NonNull AppDiscoveryUpdateState state) {
+        if (!mLauncher.isDestroyed()) {
+            mApps.onAppDiscoverySearchUpdate(app, state);
+            notifyResultChanged();
+        }
+    }
+
+    private void notifyResultChanged() {
+        mElevationController.reset();
+        mAppsRecyclerView.onSearchResultsChanged();
+    }
+
+    @Override
+    public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
+        mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                DeviceProfile dp = mLauncher.getDeviceProfile();
+                if (!dp.isVerticalBarLayout()) {
+                    Rect insets = mLauncher.getDragLayer().getInsets();
+                    int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
+                    int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
+                            + ((MarginLayoutParams) getLayoutParams()).bottomMargin;
+                    listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
+                } else {
+                    listener.onScrollRangeChanged(bottom);
+                }
+            }
+        });
+    }
+}
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
similarity index 66%
rename from src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
rename to src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 136d369..26f6ec3 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -13,20 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
 
 import android.os.Handler;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.util.ComponentKey;
 
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * The default search implementation.
  */
-public class DefaultAppSearchAlgorithm {
+public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
 
     private final List<AppInfo> mApps;
     protected final Handler mResultHandler;
@@ -36,12 +37,14 @@
         mResultHandler = new Handler();
     }
 
+    @Override
     public void cancel(boolean interruptActiveRequests) {
         if (interruptActiveRequests) {
             mResultHandler.removeCallbacksAndMessages(null);
         }
     }
 
+    @Override
     public void doSearch(final String query,
             final AllAppsSearchBarController.Callbacks callback) {
         final ArrayList<ComponentKey> result = getTitleMatchResult(query);
@@ -54,20 +57,21 @@
         });
     }
 
-    protected ArrayList<ComponentKey> getTitleMatchResult(String query) {
+    private ArrayList<ComponentKey> getTitleMatchResult(String query) {
         // Do an intersection of the words in the query and each title, and filter out all the
         // apps that don't match all of the words in the query.
         final String queryTextLower = query.toLowerCase();
         final ArrayList<ComponentKey> result = new ArrayList<>();
+        StringMatcher matcher = StringMatcher.getInstance();
         for (AppInfo info : mApps) {
-            if (matches(info, queryTextLower)) {
+            if (matches(info, queryTextLower, matcher)) {
                 result.add(info.toComponentKey());
             }
         }
         return result;
     }
 
-    protected boolean matches(AppInfo info, String query) {
+    public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
         int queryLength = query.length();
 
         String title = info.title.toString();
@@ -88,7 +92,7 @@
             nextType = i < (titleLength - 1) ?
                     Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
             if (isBreak(thisType, lastType, nextType) &&
-                    title.substring(i, i + queryLength).equalsIgnoreCase(query)) {
+                    matcher.matches(query, title.substring(i, i + queryLength))) {
                 return true;
             }
         }
@@ -103,7 +107,14 @@
      *      3) Any capital character after a digit or small character
      *      4) Any capital character before a small character
      */
-    protected boolean isBreak(int thisType, int prevType, int nextType) {
+    private static boolean isBreak(int thisType, int prevType, int nextType) {
+        switch (prevType) {
+            case Character.UNASSIGNED:
+            case Character.SPACE_SEPARATOR:
+            case Character.LINE_SEPARATOR:
+            case Character.PARAGRAPH_SEPARATOR:
+                return true;
+        }
         switch (thisType) {
             case Character.UPPERCASE_LETTER:
                 if (nextType == Character.UPPERCASE_LETTER) {
@@ -130,8 +141,44 @@
                 // Always a break point for a symbol
                 return true;
             default:
-                // Always a break point at first character
-                return  prevType == Character.UNASSIGNED;
+                return  false;
+        }
+    }
+
+    public static class StringMatcher {
+
+        private static final char MAX_UNICODE = '\uFFFF';
+
+        private final Collator mCollator;
+
+        StringMatcher() {
+            // On android N and above, Collator uses ICU implementation which has a much better
+            // support for non-latin locales.
+            mCollator = Collator.getInstance();
+            mCollator.setStrength(Collator.PRIMARY);
+            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+        }
+
+        /**
+         * Returns true if {@param query} is a prefix of {@param target}
+         */
+        public boolean matches(String query, String target) {
+            switch (mCollator.compare(query, target)) {
+                case 0:
+                    return true;
+                case -1:
+                    // The target string can contain a modifier which would make it larger than
+                    // the query string (even though the length is same). If the query becomes
+                    // larger after appending a unicode character, it was originally a prefix of
+                    // the target string and hence should match.
+                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
+                default:
+                    return false;
+            }
+        }
+
+        public static StringMatcher getInstance() {
+            return new StringMatcher();
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
new file mode 100644
index 0000000..7cd32b2
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
@@ -0,0 +1,81 @@
+package com.android.launcher3.allapps.search;
+
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
+
+/**
+ * Helper class for controlling the header elevation in response to RecyclerView scroll.
+ */
+public class HeaderElevationController extends RecyclerView.OnScrollListener {
+
+    private final View mHeader;
+    private final View mHeaderChild;
+    private final float mMaxElevation;
+    private final float mScrollToElevation;
+
+    private int mCurrentY = 0;
+
+    public HeaderElevationController(View header) {
+        mHeader = header;
+        final Resources res = mHeader.getContext().getResources();
+        mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
+        mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+        // We need to provide a custom outline so the shadow only appears on the bottom edge.
+        // The top, left and right edges are all extended out to match parent's edge, so that
+        // the shadow is clipped by the parent.
+        final ViewOutlineProvider vop = new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                // Set the left and top to be at the parents edge. Since the coordinates are
+                // relative to this view,
+                //    (x = -view.getLeft()) for this view => (x = 0) for parent
+                final int left = -view.getLeft();
+                final int top = -view.getTop();
+
+                // Since the view is centered align, the spacing on left and right are same.
+                // Add same spacing on the right to reach parent's edge.
+                final int right = view.getWidth() - left;
+                final int bottom = view.getHeight();
+                final int offset = (int) mMaxElevation;
+                outline.setRect(left - offset, top - offset, right + offset, bottom);
+            }
+        };
+        mHeader.setOutlineProvider(vop);
+        mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
+    }
+
+    public void reset() {
+        mCurrentY = 0;
+        onScroll(mCurrentY);
+    }
+
+    @Override
+    public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+        mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
+        onScroll(mCurrentY);
+    }
+
+    private void onScroll(int scrollY) {
+        float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
+        float newElevation = mMaxElevation * elevationPct;
+        if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
+            mHeader.setElevation(newElevation);
+
+            // To simulate a scrolling effect for the header, we translate the header down, and
+            // its content up by the same amount, so that it gets clipped by the parent, making it
+            // look like the content was scrolled out of the view.
+            int shift = Math.min(mHeader.getHeight(), scrollY);
+            mHeader.setTranslationY(-shift);
+            mHeaderChild.setTranslationY(shift);
+        }
+    }
+
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java b/src/com/android/launcher3/allapps/search/SearchAlgorithm.java
new file mode 100644
index 0000000..c409b1c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchAlgorithm.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.allapps.search;
+
+/**
+ * An interface for handling search.
+ */
+public interface SearchAlgorithm {
+
+    /**
+     * Performs search and sends the result to the callback.
+     */
+    void doSearch(String query, AllAppsSearchBarController.Callbacks callback);
+
+    /**
+     * Cancels any active request.
+     */
+    void cancel(boolean interruptActiveRequests);
+}
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
index 14bcd17..f0b3458 100644
--- a/src/com/android/launcher3/anim/AnimationLayerSet.java
+++ b/src/com/android/launcher3/anim/AnimationLayerSet.java
@@ -18,9 +18,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.util.ArrayMap;
 import android.view.View;
-
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
@@ -29,14 +28,14 @@
  */
 public class AnimationLayerSet extends AnimatorListenerAdapter {
 
-    private final HashMap<View, Integer> mViewsToLayerTypeMap;
+    private final ArrayMap<View, Integer> mViewsToLayerTypeMap;
 
     public AnimationLayerSet() {
-        mViewsToLayerTypeMap = new HashMap<>();
+        mViewsToLayerTypeMap = new ArrayMap<>();
     }
 
     public AnimationLayerSet(View v) {
-        mViewsToLayerTypeMap = new HashMap<>(1);
+        mViewsToLayerTypeMap = new ArrayMap<>(1);
         addView(v);
     }
 
diff --git a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
similarity index 97%
rename from src/com/android/launcher3/util/CircleRevealOutlineProvider.java
rename to src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
index 9fe5147..9fb6b49 100644
--- a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
 
 public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
 
diff --git a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java b/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
deleted file mode 100644
index be1e2d6..0000000
--- a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.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.anim;
-
-import android.graphics.Rect;
-
-import com.android.launcher3.util.PillRevealOutlineProvider;
-
-/**
- * Extension of {@link PillRevealOutlineProvider} which only changes the height of the pill.
- * For now, we assume the height is added/removed from the bottom.
- */
-public class PillHeightRevealOutlineProvider extends PillRevealOutlineProvider {
-
-    private final int mNewHeight;
-
-    public PillHeightRevealOutlineProvider(Rect pillRect, float radius, int newHeight) {
-        super(0, 0, pillRect, radius);
-        mOutline.set(pillRect);
-        mNewHeight = newHeight;
-    }
-
-    @Override
-    public void setProgress(float progress) {
-        mOutline.top = 0;
-        int heightDifference = mPillRect.height() - mNewHeight;
-        mOutline.bottom = (int) (mPillRect.bottom - heightDifference * (1 - progress));
-    }
-}
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
similarity index 96%
rename from src/com/android/launcher3/util/RevealOutlineAnimation.java
rename to src/com/android/launcher3/anim/RevealOutlineAnimation.java
index 4560477..51d00d9 100644
--- a/src/com/android/launcher3/util/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -1,4 +1,4 @@
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -83,4 +83,8 @@
     public void getOutline(View v, Outline outline) {
         outline.setRoundRect(mOutline, mOutlineRadius);
     }
+
+    public float getRadius() {
+        return mOutlineRadius;
+    }
 }
diff --git a/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
new file mode 100644
index 0000000..d01b26c
--- /dev/null
+++ b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
@@ -0,0 +1,77 @@
+/*
+ * 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.anim;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.popup.PopupContainerWithArrow;
+
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;
+
+/**
+ * A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii
+ * and two {@link Rect}s.
+ *
+ * An example usage of this provider is an outline that starts out as a circle and ends
+ * as a rounded rectangle.
+ */
+public class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {
+    private final float mStartRadius;
+    private final float mEndRadius;
+
+    private final Rect mStartRect;
+    private final Rect mEndRect;
+
+    private final @PopupContainerWithArrow.RoundedCornerFlags int mRoundedCorners;
+
+    public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+            Rect endRect) {
+        this(startRadius, endRadius, startRect, endRect,
+                ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
+    }
+
+    public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+            Rect endRect, int roundedCorners) {
+        mStartRadius = startRadius;
+        mEndRadius = endRadius;
+        mStartRect = startRect;
+        mEndRect = endRect;
+        mRoundedCorners = roundedCorners;
+    }
+
+    @Override
+    public boolean shouldRemoveElevationDuringAnimation() {
+        return false;
+    }
+
+    @Override
+    public void setProgress(float progress) {
+        mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
+
+        mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
+        mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
+        if ((mRoundedCorners & ROUNDED_TOP_CORNERS) == 0) {
+            mOutline.top -= mOutlineRadius;
+        }
+        mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
+        mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
+        if ((mRoundedCorners & ROUNDED_BOTTOM_CORNERS) == 0) {
+            mOutline.bottom += mOutlineRadius;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
new file mode 100644
index 0000000..eec3a48
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -0,0 +1,261 @@
+/*
+ * 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.anim;
+
+import android.support.animation.FloatPropertyCompat;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.IntDef;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+
+import com.android.launcher3.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Handler class that manages springs for a set of views that should all move based on the same
+ * {@link MotionEvent}s.
+ *
+ * Supports setting either X or Y velocity on the list of springs added to this handler.
+ */
+public class SpringAnimationHandler<T> {
+
+    private static final String TAG = "SpringAnimationHandler";
+    private static final boolean DEBUG = false;
+
+    private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Y_DIRECTION, X_DIRECTION})
+    public @interface Direction {}
+    public static final int Y_DIRECTION = 0;
+    public static final int X_DIRECTION = 1;
+    private int mVelocityDirection;
+
+    private VelocityTracker mVelocityTracker;
+    private float mCurrentVelocity = 0;
+    private boolean mShouldComputeVelocity = false;
+
+    private AnimationFactory<T> mAnimationFactory;
+
+    private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
+
+    /**
+     * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
+     *                  Determines which direction we use to calculate and set the velocity.
+     * @param factory   The AnimationFactory is responsible for initializing and updating the
+     *                  SpringAnimations added to this class.
+     */
+    public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
+        mVelocityDirection = direction;
+        mAnimationFactory = factory;
+    }
+
+    /**
+     * Adds a spring to the list of springs handled by this class.
+     * @param spring The new spring to be added.
+     * @param setDefaultValues If True, sets the spring to the default
+     *                         {@link AnimationFactory} values.
+     */
+    public void add(SpringAnimation spring, boolean setDefaultValues) {
+        if (setDefaultValues) {
+            mAnimationFactory.setDefaultValues(spring);
+        }
+        spring.setStartVelocity(mCurrentVelocity);
+        mAnimations.add(spring);
+    }
+
+    /**
+     * Adds a new or recycled animation to the list of springs handled by this class.
+     *
+     * @param view The view the spring is attached to.
+     * @param object Used to initialize and update the spring.
+     */
+    public void add(View view, T object) {
+        SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+        if (spring == null) {
+            spring = mAnimationFactory.initialize(object);
+            view.setTag(R.id.spring_animation_tag, spring);
+        }
+        mAnimationFactory.update(spring, object);
+        add(spring, false /* setDefaultValues */);
+    }
+
+    /**
+     * Stops and removes the spring attached to {@param view}.
+     */
+    public void remove(View view) {
+        remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
+    }
+
+    public void remove(SpringAnimation animation) {
+        if (animation.canSkipToEnd()) {
+            animation.skipToEnd();
+        }
+        mAnimations.remove(animation);
+    }
+
+    public void addMovement(MotionEvent event) {
+        int action = event.getActionMasked();
+        if (DEBUG) Log.d(TAG, "addMovement#action=" + action);
+        switch (action) {
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_DOWN:
+                reset();
+                break;
+        }
+
+        getVelocityTracker().addMovement(event);
+        mShouldComputeVelocity = true;
+    }
+
+    public void animateToFinalPosition(float position, int startValue) {
+        animateToFinalPosition(position, startValue, mShouldComputeVelocity);
+    }
+
+    /**
+     * @param position The final animation position.
+     * @param startValue < 0 if scrolling from start to end; > 0 if scrolling from end to start
+     *                   The magnitude of the number changes how the spring will move.
+     * @param setVelocity If true, we set the velocity to {@link #mCurrentVelocity} before
+     *                    starting the animation.
+     */
+    private void animateToFinalPosition(float position, int startValue, boolean setVelocity) {
+        if (DEBUG) {
+            Log.d(TAG, "animateToFinalPosition#position=" + position + ", startValue=" + startValue);
+        }
+
+        if (mShouldComputeVelocity) {
+            mCurrentVelocity = computeVelocity();
+        }
+
+        int size = mAnimations.size();
+        for (int i = 0; i < size; ++i) {
+            mAnimations.get(i).setStartValue(startValue);
+            if (setVelocity) {
+                mAnimations.get(i).setStartVelocity(mCurrentVelocity);
+            }
+            mAnimations.get(i).animateToFinalPosition(position);
+        }
+
+        reset();
+    }
+
+    /**
+     * Similar to {@link #animateToFinalPosition(float, int)}, but used in cases where we want to
+     * manually set the velocity.
+     */
+    public void animateToPositionWithVelocity(float position, int startValue, float velocity) {
+        if (DEBUG) {
+            Log.d(TAG, "animateToPosition#pos=" + position + ", start=" + startValue
+                    + ", velocity=" + velocity);
+        }
+
+        mCurrentVelocity = velocity;
+        mShouldComputeVelocity = false;
+        animateToFinalPosition(position, startValue, true);
+    }
+
+
+    public boolean isRunning() {
+        // All the animations run at the same time so we can just check the first one.
+        return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
+    }
+
+    public void skipToEnd() {
+        if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
+        if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
+
+        int size = mAnimations.size();
+        for (int i = 0; i < size; ++i) {
+            if (mAnimations.get(i).canSkipToEnd()) {
+                mAnimations.get(i).skipToEnd();
+            }
+        }
+    }
+
+    public void reset() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+        mCurrentVelocity = 0;
+        mShouldComputeVelocity = false;
+    }
+
+
+    private float computeVelocity() {
+        getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
+
+        float velocity = isVerticalDirection()
+                ? getVelocityTracker().getYVelocity()
+                : getVelocityTracker().getXVelocity();
+        velocity *= VELOCITY_DAMPING_FACTOR;
+
+        if (DEBUG) Log.d(TAG, "computeVelocity=" + velocity);
+        return velocity;
+    }
+
+    private boolean isVerticalDirection() {
+        return mVelocityDirection == Y_DIRECTION;
+    }
+
+    private VelocityTracker getVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        return mVelocityTracker;
+    }
+
+    /**
+     * This interface is used to initialize and update the SpringAnimations added to the
+     * {@link SpringAnimationHandler}.
+     *
+     * @param <T> The object that each SpringAnimation is attached to.
+     */
+    public interface AnimationFactory<T> {
+
+        /**
+         * Initializes a new Spring for {@param object}.
+         */
+        SpringAnimation initialize(T object);
+
+        /**
+         * Updates the value of {@param spring} based on {@param object}.
+         */
+        void update(SpringAnimation spring, T object);
+
+        /**
+         * Sets the factory default values for the given {@param spring}.
+         */
+        void setDefaultValues(SpringAnimation spring);
+    }
+
+    /**
+     * Helper method to create a new SpringAnimation for {@param view}.
+     */
+    public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
+        SpringAnimation spring = new SpringAnimation(view, property, finalPos);
+        spring.setSpring(new SpringForce(finalPos));
+        return spring;
+    }
+
+}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index ba1977a..c2cc215 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -107,7 +107,8 @@
         // Lazily load the background with shadow.
         Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
         if (backgroundWithShadow == null) {
-            backgroundWithShadow = ShadowGenerator.createPillWithShadow(Color.WHITE, width, mSize);
+            backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
+                    .setupBlurForSize(mSize).createPill(width, mSize);
             mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
         }
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 3efbbfb..a77a87f 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -16,15 +16,12 @@
 
 package com.android.launcher3.compat;
 
-import android.app.Activity;
-import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -43,7 +40,7 @@
     public static AppWidgetManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isAtLeastO()) {
+                if (Utilities.ATLEAST_OREO) {
                     sInstance = new AppWidgetManagerCompatVO(context.getApplicationContext());
                 } else {
                     sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
@@ -76,9 +73,6 @@
     public abstract boolean bindAppWidgetIdIfAllowed(
             int appWidgetId, AppWidgetProviderInfo info, Bundle options);
 
-    public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
-            Activity activity, AppWidgetHost host, int requestCode);
-
     public abstract LauncherAppWidgetProviderInfo findProvider(
             ComponentName provider, UserHandle user);
 
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index f239f5c..cb3bd6c 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -16,24 +16,21 @@
 
 package com.android.launcher3.compat;
 
-import android.app.Activity;
-import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.annotation.Nullable;
-import android.widget.Toast;
 
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -49,6 +46,9 @@
 
     @Override
     public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return Collections.emptyList();
+        }
         if (packageUser == null) {
             ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
             for (UserHandle user : mUserManager.getUserProfiles()) {
@@ -71,24 +71,20 @@
     @Override
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
             Bundle options) {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return false;
+        }
         return mAppWidgetManager.bindAppWidgetIdIfAllowed(
                 appWidgetId, info.getProfile(), info.provider, options);
     }
 
     @Override
-    public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
-            AppWidgetHost host, int requestCode) {
-        try {
-            host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
-        } catch (ActivityNotFoundException | SecurityException e) {
-            Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-        }
-    }
-
-    @Override
     public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) {
-        for (AppWidgetProviderInfo info : mAppWidgetManager
-                .getInstalledProvidersForProfile(user)) {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return null;
+        }
+        for (AppWidgetProviderInfo info :
+                getAllProviders(new PackageUserKey(provider.getPackageName(), user))) {
             if (info.provider.equals(provider)) {
                 return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
             }
@@ -99,6 +95,9 @@
     @Override
     public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
         HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return result;
+        }
         for (UserHandle user : mUserManager.getUserProfiles()) {
             for (AppWidgetProviderInfo info :
                     mAppWidgetManager.getInstalledProvidersForProfile(user)) {
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
index 1c48a13..44158ed 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
@@ -20,8 +20,10 @@
 import android.content.Context;
 import android.support.annotation.Nullable;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
 
+import java.util.Collections;
 import java.util.List;
 
 class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
@@ -32,6 +34,9 @@
 
     @Override
     public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) {
+        if (FeatureFlags.GO_DISABLE_WIDGETS) {
+            return Collections.emptyList();
+        }
         if (packageUser == null) {
             return super.getAllProviders(null);
         }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index e997a99..2cac536 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -25,16 +25,9 @@
 import android.os.Bundle;
 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.Utilities;
-import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.util.LooperExecuter;
 import com.android.launcher3.util.PackageUserKey;
-
 import java.util.List;
 
 public abstract class LauncherAppsCompat {
@@ -55,12 +48,12 @@
     }
 
     private static LauncherAppsCompat sInstance;
-    private static Object sInstanceLock = new Object();
+    private static final Object sInstanceLock = new Object();
 
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isAtLeastO()) {
+                if (Utilities.ATLEAST_OREO) {
                     sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
@@ -88,62 +81,6 @@
     public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
             @Nullable PackageUserKey packageUser);
 
-    /**
-     * request.accept() will initiate the following flow:
-     *      -> go-to-system-process for actual processing (a)
-     *      -> callback-to-launcher on UI thread (b)
-     *      -> post callback on the worker thread (c)
-     *      -> Update model and unpin (in system) any shortcut not in out model. (d)
-     *
-     * Note that (b) will take at-least one frame as it involves posting callback from binder
-     * 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
-     * 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(
-            Context context, final PinItemRequestCompat request, final long acceptDelay) {
-        if (request != null &&
-                request.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT &&
-                request.isValid()) {
-
-            if (acceptDelay <= 0) {
-                if (!request.accept()) {
-                    return null;
-                }
-            } else {
-                // Block the worker thread until the accept() is called.
-                new LooperExecuter(LauncherModel.getWorkerLooper()).execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            Thread.sleep(acceptDelay);
-                        } catch (InterruptedException e) {
-                            // Ignore
-                        }
-                        if (request.isValid()) {
-                            request.accept();
-                        }
-                    }
-                });
-            }
-
-            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
-            ShortcutInfo info = new ShortcutInfo(compat, context);
-            // Apply the unbadged icon and fetch the actual icon asynchronously.
-            info.iconBitmap = LauncherIcons
-                    .createShortcutIcon(compat, context, false /* badged */);
-            LauncherAppState.getInstance(context).getModel()
-                    .updateAndBindShortcutInfo(info, compat);
-            return info;
-        } else {
-            return null;
-        }
-    }
-
     public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
         showAppDetailsForProfile(component, user, null, null);
     }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 647c315..cc3e5a7 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -29,23 +29,22 @@
 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.HashMap;
 import java.util.List;
-import java.util.Map;
 
 public class LauncherAppsCompatVL extends LauncherAppsCompat {
 
     protected final LauncherApps mLauncherApps;
     protected final Context mContext;
 
-    private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks = new HashMap<>();
+    private final ArrayMap<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks =
+        new ArrayMap<>();
 
     LauncherAppsCompatVL(Context context) {
         mContext = context;
@@ -131,43 +130,52 @@
     }
 
     private static class WrappedCallback extends LauncherApps.Callback {
-        private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
+        private final LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
 
         public WrappedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
             mCallback = callback;
         }
 
+        @Override
         public void onPackageRemoved(String packageName, UserHandle user) {
             mCallback.onPackageRemoved(packageName, user);
         }
 
+        @Override
         public void onPackageAdded(String packageName, UserHandle user) {
             mCallback.onPackageAdded(packageName, user);
         }
 
+        @Override
         public void onPackageChanged(String packageName, UserHandle user) {
             mCallback.onPackageChanged(packageName, user);
         }
 
+        @Override
         public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
             mCallback.onPackagesAvailable(packageNames, user, replacing);
         }
 
+        @Override
         public void onPackagesUnavailable(String[] packageNames, UserHandle user,
                 boolean replacing) {
             mCallback.onPackagesUnavailable(packageNames, user, replacing);
         }
 
+        @Override
         public void onPackagesSuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesSuspended(packageNames, user);
         }
 
+        @Override
         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesUnsuspended(packageNames, user);
         }
 
-        public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
-                UserHandle user) {
+        @Override
+        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));
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index d145539..3214b46 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -18,20 +18,27 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
 import android.support.annotation.Nullable;
-import android.util.Log;
 
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageUserKey;
 
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -45,11 +52,6 @@
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
         try {
-            // TODO: Temporary workaround until the API signature is updated
-            if (false) {
-                throw new PackageManager.NameNotFoundException();
-            }
-
             ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
             return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
                     ? null : info;
@@ -64,34 +66,89 @@
         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
         UserHandle myUser = Process.myUserHandle();
 
-        try {
-            Method m = LauncherApps.class.getDeclaredMethod("getShortcutConfigActivityList",
-                    String.class, UserHandle.class);
-            final List<UserHandle> users;
-            final String packageName;
-            if (packageUser == null) {
-                users = UserManagerCompat.getInstance(mContext).getUserProfiles();
-                packageName = null;
-            } else {
-                users = new ArrayList<>(1);
-                users.add(packageUser.mUser);
-                packageName = packageUser.mPackageName;
-            }
-            for (UserHandle user : users) {
-                boolean ignoreTargetSdk = myUser.equals(user);
-                List<LauncherActivityInfo> activities =
-                        (List<LauncherActivityInfo>) m.invoke(mLauncherApps, packageName, user);
-                for (LauncherActivityInfo activityInfo : activities) {
-                    if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
-                            Build.VERSION_CODES.O) {
-                        result.add(new ShortcutConfigActivityInfoVO(activityInfo));
-                    }
+        final List<UserHandle> users;
+        final String packageName;
+        if (packageUser == null) {
+            users = UserManagerCompat.getInstance(mContext).getUserProfiles();
+            packageName = null;
+        } else {
+            users = new ArrayList<>(1);
+            users.add(packageUser.mUser);
+            packageName = packageUser.mPackageName;
+        }
+        for (UserHandle user : users) {
+            boolean ignoreTargetSdk = myUser.equals(user);
+            List<LauncherActivityInfo> activities =
+                    mLauncherApps.getShortcutConfigActivityList(packageName, user);
+            for (LauncherActivityInfo activityInfo : activities) {
+                if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
+                        Build.VERSION_CODES.O) {
+                    result.add(new ShortcutConfigActivityInfoVO(activityInfo));
                 }
             }
-        } catch (Exception e) {
-            Log.e("LauncherAppsCompatVO", "Error calling new API", e);
         }
 
         return result;
     }
+
+    /**
+     * request.accept() will initiate the following flow:
+     *      -> go-to-system-process for actual processing (a)
+     *      -> callback-to-launcher on UI thread (b)
+     *      -> post callback on the worker thread (c)
+     *      -> Update model and unpin (in system) any shortcut not in out model. (d)
+     *
+     * Note that (b) will take at-least one frame as it involves posting callback from binder
+     * 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
+     * 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(
+            Context context, final PinItemRequest request, final long acceptDelay) {
+        if (request != null &&
+                request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
+                request.isValid()) {
+
+            if (acceptDelay <= 0) {
+                if (!request.accept()) {
+                    return null;
+                }
+            } else {
+                // Block the worker thread until the accept() is called.
+                new LooperExecutor(LauncherModel.getWorkerLooper()).execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            Thread.sleep(acceptDelay);
+                        } catch (InterruptedException e) {
+                            // Ignore
+                        }
+                        if (request.isValid()) {
+                            request.accept();
+                        }
+                    }
+                });
+            }
+
+            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
+            ShortcutInfo info = new ShortcutInfo(compat, context);
+            // Apply the unbadged icon and fetch the actual icon asynchronously.
+            info.iconBitmap = LauncherIcons
+                    .createShortcutIcon(compat, context, false /* badged */);
+            LauncherAppState.getInstance(context).getModel()
+                    .updateAndBindShortcutInfo(info, compat);
+            return info;
+        } else {
+            return null;
+        }
+    }
+
+    public static PinItemRequest getPinItemRequest(Intent intent) {
+        Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
+        return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
+    }
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index c7fe0ce..112cca5 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -16,9 +16,13 @@
 
 package com.android.launcher3.compat;
 
+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;
 
 public abstract class PackageInstallerCompat {
 
@@ -46,19 +50,34 @@
     public abstract void onStop();
 
     public static final class PackageInstallInfo {
+        public final ComponentName componentName;
         public final String packageName;
+        public final int state;
+        public final int progress;
 
-        public int state;
-        public int progress;
-
-        public PackageInstallInfo(String packageName) {
-            this.packageName = packageName;
+        private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
+            this.state = STATUS_INSTALLING;
+            this.packageName = info.getAppPackageName();
+            this.componentName = new ComponentName(packageName, "");
+            this.progress = (int) (info.getProgress() * 100f);
         }
 
         public PackageInstallInfo(String packageName, int state, int progress) {
-            this.packageName = packageName;
             this.state = state;
+            this.packageName = packageName;
+            this.componentName = new ComponentName(packageName, "");
             this.progress = progress;
         }
+
+        public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+            return new PackageInstallInfo(info);
+        }
+
+        public static PackageInstallInfo fromState(int state, String packageName) {
+            return new PackageInstallInfo(packageName, state, 0 /* progress */);
+        }
+
     }
+
+    public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index b87582f..1ffd3da 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -17,34 +17,44 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionCallback;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.SparseArray;
 
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 
 public class PackageInstallerCompatVL extends PackageInstallerCompat {
 
+    private static final boolean DEBUG = false;
+
     @Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
 
     @Thunk final PackageInstaller mInstaller;
     private final IconCache mCache;
     private final Handler mWorker;
+    private final Context mAppContext;
+    private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
 
     PackageInstallerCompatVL(Context context) {
+        mAppContext = context.getApplicationContext();
         mInstaller = context.getPackageManager().getPackageInstaller();
         mCache = LauncherAppState.getInstance(context).getIconCache();
         mWorker = new Handler(LauncherModel.getWorkerLooper());
-
         mInstaller.registerSessionCallback(mCallback, mWorker);
     }
 
@@ -52,7 +62,7 @@
     public HashMap<String, Integer> updateAndGetActiveSessionCache() {
         HashMap<String, Integer> activePackages = new HashMap<>();
         UserHandle user = Process.myUserHandle();
-        for (SessionInfo info : mInstaller.getAllSessions()) {
+        for (SessionInfo info : getAllVerifiedSessions()) {
             addSessionInfoToCache(info, user);
             if (info.getAppPackageName() != null) {
                 activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
@@ -86,7 +96,14 @@
 
         @Override
         public void onCreated(int sessionId) {
-            pushSessionDisplayToLauncher(sessionId);
+            SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+            if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
+                LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+                if (app != null) {
+                    app.getModel().onInstallSessionCreated(
+                            PackageInstallInfo.fromInstallingState(sessionInfo));
+                }
+            }
         }
 
         @Override
@@ -97,18 +114,17 @@
             mActiveSessions.remove(sessionId);
 
             if (packageName != null) {
-                sendUpdate(new PackageInstallInfo(packageName,
-                        success ? STATUS_INSTALLED : STATUS_FAILED, 0));
+                sendUpdate(PackageInstallInfo.fromState(
+                        success ? STATUS_INSTALLED : STATUS_FAILED,
+                        packageName));
             }
         }
 
         @Override
         public void onProgressChanged(int sessionId, float progress) {
-            SessionInfo session = mInstaller.getSessionInfo(sessionId);
+            SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
             if (session != null && session.getAppPackageName() != null) {
-                sendUpdate(new PackageInstallInfo(session.getAppPackageName(),
-                        STATUS_INSTALLING,
-                        (int) (session.getProgress() * 100)));
+                sendUpdate(PackageInstallInfo.fromInstallingState(session));
             }
         }
 
@@ -120,16 +136,48 @@
             pushSessionDisplayToLauncher(sessionId);
         }
 
-        private void pushSessionDisplayToLauncher(int sessionId) {
-            SessionInfo session = mInstaller.getSessionInfo(sessionId);
+        private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
+            SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
             if (session != null && session.getAppPackageName() != null) {
+                mActiveSessions.put(sessionId, session.getAppPackageName());
                 addSessionInfoToCache(session, Process.myUserHandle());
                 LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-
                 if (app != null) {
                     app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
                 }
+                return session;
             }
+            return null;
         }
     };
+
+    private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
+        if (sessionInfo == null
+                || sessionInfo.getInstallerPackageName() == null
+                || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
+            return null;
+        }
+        String pkg = sessionInfo.getInstallerPackageName();
+        synchronized (mSessionVerifiedMap) {
+            if (!mSessionVerifiedMap.containsKey(pkg)) {
+                LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
+                boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
+                        ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
+                mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
+            }
+        }
+        return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
+    }
+
+    @Override
+    public List<SessionInfo> getAllVerifiedSessions() {
+        List<SessionInfo> list = new ArrayList<>(mInstaller.getAllSessions());
+        Iterator<SessionInfo> it = list.iterator();
+        while (it.hasNext()) {
+            if (verify(it.next()) == null) {
+                it.remove();
+            }
+        }
+        return list;
+    }
 }
diff --git a/src/com/android/launcher3/compat/PinItemRequestCompat.java b/src/com/android/launcher3/compat/PinItemRequestCompat.java
deleted file mode 100644
index 1308cba..0000000
--- a/src/com/android/launcher3/compat/PinItemRequestCompat.java
+++ /dev/null
@@ -1,126 +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.compat;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.launcher3.Utilities;
-
-/**
- * A wrapper around platform implementation of PinItemRequestCompat until the
- * updated SDK is available.
- */
-public class PinItemRequestCompat implements Parcelable {
-
-    public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
-
-    public static final int REQUEST_TYPE_SHORTCUT = 1;
-    public static final int REQUEST_TYPE_APPWIDGET = 2;
-
-    private final Parcelable mObject;
-
-    private PinItemRequestCompat(Parcelable object) {
-        mObject = object;
-    }
-
-    public int getRequestType() {
-        return (Integer) invokeMethod("getRequestType");
-    }
-
-    public ShortcutInfo getShortcutInfo() {
-        return (ShortcutInfo) invokeMethod("getShortcutInfo");
-    }
-
-    public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
-        try {
-            return (AppWidgetProviderInfo) mObject.getClass()
-                    .getDeclaredMethod("getAppWidgetProviderInfo", Context.class)
-                    .invoke(mObject, context);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public boolean isValid() {
-        return (Boolean) invokeMethod("isValid");
-    }
-
-    public boolean accept() {
-        return (Boolean) invokeMethod("accept");
-    }
-
-    public boolean accept(Bundle options) {
-        try {
-            return (Boolean) mObject.getClass().getDeclaredMethod("accept", Bundle.class)
-                    .invoke(mObject, options);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public Bundle getExtras() {
-        try {
-            return (Bundle) mObject.getClass().getDeclaredMethod("getExtras").invoke(mObject);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    private Object invokeMethod(String methodName) {
-        try {
-            return mObject.getClass().getDeclaredMethod(methodName).invoke(mObject);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int i) {
-        parcel.writeParcelable(mObject, i);
-    }
-
-    public static final Parcelable.Creator<PinItemRequestCompat> CREATOR =
-            new Parcelable.Creator<PinItemRequestCompat>() {
-                public PinItemRequestCompat createFromParcel(Parcel source) {
-                    Parcelable object = source.readParcelable(null);
-                    return new PinItemRequestCompat(object);
-                }
-
-                public PinItemRequestCompat[] newArray(int size) {
-                    return new PinItemRequestCompat[size];
-                }
-            };
-
-    public static PinItemRequestCompat getPinItemRequest(Intent intent) {
-        if (!Utilities.isAtLeastO()) {
-            return null;
-        }
-        Parcelable extra = intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
-        return extra == null ? null : new PinItemRequestCompat(extra);
-    }
-}
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index 4a55e8c..31c0087 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -37,8 +37,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 
-import java.lang.reflect.Method;
-
 /**
  * Wrapper class for representing a shortcut configure activity.
  */
@@ -127,7 +125,7 @@
     }
 
     @TargetApi(26)
-    static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
+    public static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
 
         private final LauncherActivityInfo mInfo;
 
@@ -151,15 +149,13 @@
             if (getUser().equals(Process.myUserHandle())) {
                 return super.startConfigActivity(activity, requestCode);
             }
+            IntentSender is = activity.getSystemService(LauncherApps.class)
+                    .getShortcutConfigActivityIntent(mInfo);
             try {
-                Method m = LauncherApps.class.getDeclaredMethod(
-                        "getShortcutConfigActivityIntent", LauncherActivityInfo.class);
-                IntentSender is = (IntentSender) m.invoke(
-                        activity.getSystemService(LauncherApps.class), mInfo);
                 activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0);
                 return true;
-            } catch (Exception e) {
-                Log.e(TAG, "Error calling new API", e);
+            } catch (IntentSender.SendIntentException e) {
+                Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
                 return false;
             }
         }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 45525f5..bb42573 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -21,13 +21,11 @@
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-
-import com.android.launcher3.Utilities;
+import android.util.ArrayMap;
 import com.android.launcher3.util.LongArrayMap;
-
+import com.android.launcher3.util.ManagedProfileHeuristic;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 public class UserManagerCompatVL extends UserManagerCompat {
@@ -40,7 +38,7 @@
     protected LongArrayMap<UserHandle> mUsers;
     // Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same
     // and not {@link Object#equals}
-    protected HashMap<UserHandle, Long> mUserToSerialMap;
+    protected ArrayMap<UserHandle, Long> mUserToSerialMap;
 
     UserManagerCompatVL(Context context) {
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -88,7 +86,7 @@
     public void enableAndResetCache() {
         synchronized (this) {
             mUsers = new LongArrayMap<>();
-            mUserToSerialMap = new HashMap<>();
+            mUserToSerialMap = new ArrayMap<>();
             List<UserHandle> users = mUserManager.getUserProfiles();
             if (users != null) {
                 for (UserHandle user : users) {
@@ -122,7 +120,7 @@
 
     @Override
     public long getUserCreationTime(UserHandle user) {
-        SharedPreferences prefs = Utilities.getPrefs(mContext);
+        SharedPreferences prefs = ManagedProfileHeuristic.prefs(mContext);
         String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
         if (!prefs.contains(key)) {
             prefs.edit().putLong(key, System.currentTimeMillis()).apply();
diff --git a/src/com/android/launcher3/compat/WallpaperColorsCompat.java b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
new file mode 100644
index 0000000..e25b9d9
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
@@ -0,0 +1,55 @@
+/*
+ * 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.compat;
+
+/**
+ * A compatibility layer around platform implementation of WallpaperColors
+ */
+public class WallpaperColorsCompat {
+
+    public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
+    public static final int HINT_SUPPORTS_DARK_THEME = 0x2;
+
+    private final int mPrimaryColor;
+    private final int mSecondaryColor;
+    private final int mTertiaryColor;
+    private final int mColorHints;
+
+    public WallpaperColorsCompat(int primaryColor, int secondaryColor, int tertiaryColor,
+            int colorHints) {
+        mPrimaryColor = primaryColor;
+        mSecondaryColor = secondaryColor;
+        mTertiaryColor = tertiaryColor;
+        mColorHints = colorHints;
+    }
+
+    public int getPrimaryColor() {
+        return mPrimaryColor;
+    }
+
+    public int getSecondaryColor() {
+        return mSecondaryColor;
+    }
+
+    public int getTertiaryColor() {
+        return mTertiaryColor;
+    }
+
+    public int getColorHints() {
+        return mColorHints;
+    }
+
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
new file mode 100644
index 0000000..00258c7
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+
+public abstract class WallpaperManagerCompat {
+
+    private static final Object sInstanceLock = new Object();
+    private static WallpaperManagerCompat sInstance;
+
+    public static WallpaperManagerCompat getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                context = context.getApplicationContext();
+
+                if (Utilities.ATLEAST_OREO) {
+                    try {
+                        sInstance = new WallpaperManagerCompatVOMR1(context);
+                    } catch (Throwable e) {
+                        // The wallpaper APIs do not yet exist
+                    }
+                }
+                if (sInstance == null) {
+                    sInstance = new WallpaperManagerCompatVL(context);
+                }
+            }
+            return sInstance;
+        }
+    }
+
+
+    public abstract @Nullable WallpaperColorsCompat getWallpaperColors(int which);
+
+    public abstract void addOnColorsChangedListener(OnColorsChangedListenerCompat listener);
+
+    /**
+     * Interface definition for a callback to be invoked when colors change on a wallpaper.
+     */
+    public interface OnColorsChangedListenerCompat {
+
+        void onColorsChanged(WallpaperColorsCompat colors, int which);
+    }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
new file mode 100644
index 0000000..8e572ee
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -0,0 +1,292 @@
+/*
+ * 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.compat;
+
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.Nullable;
+import android.support.v7.graphics.Palette;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.launcher3.Utilities;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
+public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
+
+    private static final String TAG = "WMCompatVL";
+
+    private static final String VERSION_PREFIX = "1,";
+    private static final String KEY_COLORS = "wallpaper_parsed_colors";
+    private static final String ACTION_EXTRACTION_COMPLETE =
+            "com.android.launcher3.compat.WallpaperManagerCompatVL.EXTRACTION_COMPLETE";
+
+    private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
+
+    private final Context mContext;
+    private WallpaperColorsCompat mColorsCompat;
+
+    WallpaperManagerCompatVL(Context context) {
+        mContext = context;
+
+        String colors = getDevicePrefs(mContext).getString(KEY_COLORS, "");
+        int wallpaperId = -1;
+        if (colors.startsWith(VERSION_PREFIX)) {
+            Pair<Integer, WallpaperColorsCompat> storedValue = parseValue(colors);
+            wallpaperId = storedValue.first;
+            mColorsCompat = storedValue.second;
+        }
+
+        if (wallpaperId == -1 || wallpaperId != getWallpaperId(context)) {
+            reloadColors();
+        }
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                reloadColors();
+            }
+        }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+
+        // Register a receiver for results
+        String permission = null;
+        // Find a permission which only we can use.
+        try {
+            for (PermissionInfo info : context.getPackageManager().getPackageInfo(
+                    context.getPackageName(),
+                    PackageManager.GET_PERMISSIONS).permissions) {
+                if ((info.protectionLevel & PermissionInfo.PROTECTION_SIGNATURE) != 0) {
+                    permission = info.name;
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Something went wrong. ignore
+            Log.d(TAG, "Unable to get permission info", e);
+        }
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                handleResult(intent.getStringExtra(KEY_COLORS));
+            }
+        }, new IntentFilter(ACTION_EXTRACTION_COMPLETE), permission, new Handler());
+    }
+
+    @Nullable
+    @Override
+    public WallpaperColorsCompat getWallpaperColors(int which) {
+        return which == FLAG_SYSTEM ? mColorsCompat : null;
+    }
+
+    @Override
+    public void addOnColorsChangedListener(OnColorsChangedListenerCompat listener) {
+        mListeners.add(listener);
+    }
+
+    private void reloadColors() {
+        JobInfo job = new JobInfo.Builder(Utilities.WALLPAPER_COMPAT_JOB_ID,
+                new ComponentName(mContext, ColorExtractionService.class))
+                .setMinimumLatency(0).build();
+        ((JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(job);
+    }
+
+    private void handleResult(String result) {
+        getDevicePrefs(mContext).edit().putString(KEY_COLORS, result).apply();
+        mColorsCompat = parseValue(result).second;
+        for (OnColorsChangedListenerCompat listener : mListeners) {
+            listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM);
+        }
+    }
+
+    private static final int getWallpaperId(Context context) {
+        if (!Utilities.ATLEAST_NOUGAT) {
+            return -1;
+        }
+        return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
+    }
+
+    /**
+     * Parses the stored value and returns the wallpaper id and wallpaper colors.
+     */
+    private static Pair<Integer, WallpaperColorsCompat> parseValue(String value) {
+        String[] parts = value.split(",");
+        Integer wallpaperId = Integer.parseInt(parts[1]);
+        if (parts.length == 2) {
+            // There is no wallpaper color info present, eg when live wallpaper has no preview.
+            return Pair.create(wallpaperId, null);
+        }
+
+        int primary = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
+        int secondary = parts.length > 3 ? Integer.parseInt(parts[3]) : 0;
+        int tertiary = parts.length > 4 ? Integer.parseInt(parts[4]) : 0;
+
+        return Pair.create(wallpaperId, new WallpaperColorsCompat(primary, secondary, tertiary,
+                0 /* hints */));
+    }
+
+    /**
+     * Intent service to handle color extraction
+     */
+    public static class ColorExtractionService extends JobService implements Runnable {
+        private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
+
+        private HandlerThread mWorkerThread;
+        private Handler mWorkerHandler;
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            mWorkerThread = new HandlerThread("ColorExtractionService");
+            mWorkerThread.start();
+            mWorkerHandler = new Handler(mWorkerThread.getLooper());
+        }
+
+        @Override
+        public void onDestroy() {
+            super.onDestroy();
+            mWorkerThread.quit();
+        }
+
+        @Override
+        public boolean onStartJob(final JobParameters jobParameters) {
+            mWorkerHandler.post(this);
+            return true;
+        }
+
+        @Override
+        public boolean onStopJob(JobParameters jobParameters) {
+            mWorkerHandler.removeCallbacksAndMessages(null);
+            return true;
+        }
+
+        /**
+         * Extracts the wallpaper colors and sends the result back through the receiver.
+         */
+        @Override
+        public void run() {
+            int wallpaperId = getWallpaperId(this);
+
+            Bitmap bitmap = null;
+            Drawable drawable = null;
+
+            WallpaperManager wm = WallpaperManager.getInstance(this);
+            WallpaperInfo info = wm.getWallpaperInfo();
+            if (info != null) {
+                // For live wallpaper, extract colors from thumbnail
+                drawable = info.loadThumbnail(getPackageManager());
+            } else {
+                if (Utilities.ATLEAST_NOUGAT) {
+                    try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
+                        BitmapRegionDecoder decoder = BitmapRegionDecoder
+                                .newInstance(fd.getFileDescriptor(), false);
+
+                        int requestedArea = decoder.getWidth() * decoder.getHeight();
+                        BitmapFactory.Options options = new BitmapFactory.Options();
+
+                        if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+                            double areaRatio =
+                                    (double) requestedArea / MAX_WALLPAPER_EXTRACTION_AREA;
+                            double nearestPowOf2 =
+                                    Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
+                            options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
+                        }
+                        Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
+                        bitmap = decoder.decodeRegion(region, options);
+                        decoder.recycle();
+                    } catch (IOException | NullPointerException e) {
+                        Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+                    }
+                }
+                if (bitmap == null) {
+                    drawable = wm.getDrawable();
+                }
+            }
+
+            if (drawable != null) {
+                // Calculate how big the bitmap needs to be.
+                // This avoids unnecessary processing and allocation inside Palette.
+                final int requestedArea = drawable.getIntrinsicWidth() *
+                        drawable.getIntrinsicHeight();
+                double scale = 1;
+                if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+                    scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
+                }
+                bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * scale),
+                        (int) (drawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888);
+                final Canvas bmpCanvas = new Canvas(bitmap);
+                drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+                drawable.draw(bmpCanvas);
+            }
+
+            String value = VERSION_PREFIX + wallpaperId;
+
+            if (bitmap != null) {
+                Palette palette = Palette.from(bitmap).generate();
+                bitmap.recycle();
+
+                StringBuilder builder = new StringBuilder(value);
+                List<Pair<Integer,Integer>> colorsToOccurrences = new ArrayList<>();
+                for (Palette.Swatch swatch : palette.getSwatches()) {
+                    colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation()));
+                }
+
+                Collections.sort(colorsToOccurrences, new Comparator<Pair<Integer, Integer>>() {
+                    @Override
+                    public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
+                        return b.second - a.second;
+                    }
+                });
+
+                for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) {
+                    builder.append(',').append(colorsToOccurrences.get(i).first);
+                }
+
+                value = builder.toString();
+            }
+
+            // Send the result
+            sendBroadcast(new Intent(ACTION_EXTRACTION_COMPLETE)
+                    .setPackage(getPackageName())
+                    .putExtra(KEY_COLORS, value));
+        }
+    }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
new file mode 100644
index 0000000..524f266
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -0,0 +1,85 @@
+/*
+ * 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.compat;
+
+import android.annotation.TargetApi;
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
+import android.app.WallpaperManager.OnColorsChangedListener;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+
+@TargetApi(27)
+public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
+
+    private static final String TAG = "WMCompatVOMR1";
+
+    private final WallpaperManager mWm;
+    private Method mWCColorHintsMethod;
+
+    WallpaperManagerCompatVOMR1(Context context) throws Throwable {
+        mWm = context.getSystemService(WallpaperManager.class);
+        String className = WallpaperColors.class.getName();
+        try {
+            mWCColorHintsMethod = WallpaperColors.class.getDeclaredMethod("getColorHints");
+        } catch (Exception exc) {
+            Log.e(TAG, "getColorHints not available", exc);
+        }
+    }
+
+    @Nullable
+    @Override
+    public WallpaperColorsCompat getWallpaperColors(int which) {
+        return convertColorsObject(mWm.getWallpaperColors(which));
+    }
+
+    @Override
+    public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
+        OnColorsChangedListener onChangeListener = new OnColorsChangedListener() {
+            @Override
+            public void onColorsChanged(WallpaperColors colors, int which) {
+                listener.onColorsChanged(convertColorsObject(colors), which);
+            }
+        };
+        mWm.addOnColorsChangedListener(onChangeListener, null);
+    }
+
+    private WallpaperColorsCompat convertColorsObject(WallpaperColors colors) {
+        if (colors == null) {
+            return null;
+        }
+        Color primary = colors.getPrimaryColor();
+        Color secondary = colors.getSecondaryColor();
+        Color tertiary = colors.getTertiaryColor();
+        int primaryVal = primary != null ? primary.toArgb() : 0;
+        int secondaryVal = secondary != null ? secondary.toArgb() : 0;
+        int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
+        int colorHints = 0;
+        try {
+            if (mWCColorHintsMethod != null) {
+                colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+            }
+        } catch (Exception exc) {
+            Log.e(TAG, "error calling color hints", exc);
+        }
+        return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
+    }
+}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
new file mode 100644
index 0000000..6a4cbcb
--- /dev/null
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ *
+ * All the flags should be defined here with appropriate default values. To override a value,
+ * redefine it in {@link FeatureFlags}.
+ *
+ * This class is kept package-private to prevent direct access.
+ */
+abstract class BaseFlags {
+
+    BaseFlags() {}
+
+    public static final boolean IS_DOGFOOD_BUILD = false;
+
+    // Custom flags go below this
+    public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
+    public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
+    public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
+    public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
+    // When enabled allows to use any point on the fast scrollbar to start dragging.
+    public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
+    // When enabled while all-apps open, the soft input will be set to adjust resize .
+    public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
+    // 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 uses the AllAppsRadialGradientAndScrimDrawable for all apps
+    public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
+    // When enabled allows use of physics based motions in the Launcher.
+    public static final boolean LAUNCHER3_PHYSICS = true;
+    // 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.
+    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;
+    // When enabled fling down gesture on the first workspace triggers search.
+    public static final boolean PULLDOWN_SEARCH = false;
+    // When enabled the status bar may show dark icons based on the top of the wallpaper.
+    public static final boolean LIGHT_STATUS_BAR = false;
+    // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
+    public static final boolean LEGACY_ICON_TREATMENT = true;
+    // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
+    public static final boolean ADAPTIVE_ICON_SHADOW = true;
+    // When enabled, app discovery will be enabled if service is implemented
+    public static final boolean DISCOVERY_ENABLED = false;
+    // When enabled, the qsb will be moved to the hotseat.
+    public static final boolean QSB_IN_HOTSEAT = true;
+
+    // Features to control Launcher3Go behavior
+    public static final boolean GO_DISABLE_WIDGETS = false;
+}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItem.java b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
index 09c91ac..2e48b25 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryItem.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.discovery;
 
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 09592a8..c843e72 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,18 +16,13 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-
 import android.annotation.TargetApi;
 import android.app.ActivityOptions;
-import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
+import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Point;
@@ -44,13 +39,13 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+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;
@@ -60,7 +55,12 @@
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetImageView;
 
-@TargetApi(Build.VERSION_CODES.N_MR1)
+import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+
+@TargetApi(Build.VERSION_CODES.O)
 public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
 
     private static final int SHADOW_SIZE = 10;
@@ -70,24 +70,26 @@
 
     private final PointF mLastTouchPos = new PointF();
 
-    private PinItemRequestCompat mRequest;
+    private PinItemRequest mRequest;
     private LauncherAppState mApp;
     private InvariantDeviceProfile mIdp;
 
     private LivePreviewWidgetCell mWidgetCell;
 
     // Widget request specific options.
-    private AppWidgetHost mAppWidgetHost;
+    private LauncherAppWidgetHost mAppWidgetHost;
     private AppWidgetManagerCompat mAppWidgetManager;
     private PendingAddWidgetInfo mPendingWidgetInfo;
     private int mPendingBindWidgetId;
     private Bundle mWidgetOptions;
 
+    private boolean mFinishOnPause = false;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mRequest = PinItemRequestCompat.getPinItemRequest(getIntent());
+        mRequest = LauncherAppsCompatVO.getPinItemRequest(getIntent());
         if (mRequest == null) {
             finish();
             return;
@@ -101,9 +103,9 @@
         mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());
 
         setContentView(R.layout.add_item_confirmation_activity);
-        mWidgetCell = (LivePreviewWidgetCell) findViewById(R.id.widget_cell);
+        mWidgetCell = findViewById(R.id.widget_cell);
 
-        if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
             setupShortcut();
         } else {
             if (!setupWidget()) {
@@ -162,6 +164,7 @@
 
         startActivity(homeIntent,
                 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+        mFinishOnPause = true;
 
         // Start a system drag and drop. We use a transparent bitmap as preview for system drag
         // as the preview is handled internally by launcher.
@@ -181,6 +184,14 @@
         return false;
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mFinishOnPause) {
+            finish();
+        }
+    }
+
     private void setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
@@ -200,7 +211,7 @@
         mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest));
 
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
-        mAppWidgetHost = new AppWidgetHost(this, Launcher.APPWIDGET_HOST_ID);
+        mAppWidgetHost = new LauncherAppWidgetHost(this);
 
         mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
         mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
@@ -226,7 +237,7 @@
      * Called when place-automatically button is clicked.
      */
     public void onPlaceAutomaticallyClick(View v) {
-        if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
             InstallShortcutReceiver.queueShortcut(
                     new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
             logCommand(Action.Command.CONFIRM);
@@ -244,13 +255,8 @@
         }
 
         // request bind widget
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
-                mPendingWidgetInfo.componentName);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
-                mRequest.getAppWidgetProviderInfo(this).getProfile());
-        startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
+        mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
+                mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
     }
 
     private void acceptWidget(int widgetId) {
@@ -268,7 +274,7 @@
     }
 
     @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == REQUEST_BIND_APPWIDGET) {
             int widgetId = data != null
                     ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId)
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
new file mode 100644
index 0000000..d0f2629
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -0,0 +1,214 @@
+/*
+ * 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.dragndrop;
+
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.widget.PendingItemDragHelper;
+
+import java.util.UUID;
+
+/**
+ * {@link DragSource} for handling drop from a different window.
+ */
+public abstract class BaseItemDragListener implements
+        View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+
+    private static final String TAG = "BaseItemDragListener";
+
+    private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
+    public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
+
+    // Position of preview relative to the touch location
+    private final Rect mPreviewRect;
+
+    private final int mPreviewBitmapWidth;
+    private final int mPreviewViewWidth;
+
+    // Randomly generated id used to verify the drag event.
+    private final String mId;
+
+    protected Launcher mLauncher;
+    private DragController mDragController;
+    private long mDragStartTime;
+
+    public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
+        mPreviewRect = previewRect;
+        mPreviewBitmapWidth = previewBitmapWidth;
+        mPreviewViewWidth = previewViewWidth;
+        mId = UUID.randomUUID().toString();
+    }
+
+    protected BaseItemDragListener(Parcel parcel) {
+        mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
+        mPreviewBitmapWidth = parcel.readInt();
+        mPreviewViewWidth = parcel.readInt();
+        mId = parcel.readString();
+    }
+
+    protected void writeToParcel(Parcel parcel, int i) {
+        mPreviewRect.writeToParcel(parcel, i);
+        parcel.writeInt(mPreviewBitmapWidth);
+        parcel.writeInt(mPreviewViewWidth);
+        parcel.writeString(mId);
+    }
+
+    public String getMimeType() {
+        return MIME_TYPE_PREFIX + mId;
+    }
+
+    public void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
+        mDragController = launcher.getDragController();
+    }
+
+    @Override
+    public boolean onDrag(View view, DragEvent event) {
+        if (mLauncher == null || mDragController == null) {
+            postCleanup();
+            return false;
+        }
+        if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+            if (onDragStart(event)) {
+                return true;
+            } else {
+                postCleanup();
+                return false;
+            }
+        }
+        return mDragController.onDragEvent(mDragStartTime, event);
+    }
+
+    protected boolean onDragStart(DragEvent event) {
+        ClipDescription desc =  event.getClipDescription();
+        if (desc == null || !desc.hasMimeType(getMimeType())) {
+            Log.e(TAG, "Someone started a dragAndDrop before us.");
+            return false;
+        }
+
+        Point downPos = new Point((int) event.getX(), (int) event.getY());
+        DragOptions options = new DragOptions();
+        options.systemDndStartPoint = downPos;
+        options.preDragCondition = this;
+
+        // 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,
+        // and the absolute position (position relative to the screen) of drag event is same
+        // 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);
+        mDragStartTime = SystemClock.uptimeMillis();
+        return true;
+    }
+
+    protected abstract PendingItemDragHelper createDragHelper();
+
+    @Override
+    public boolean shouldStartDrag(double distanceDragged) {
+        // Stay in pre-drag mode, if workspace is locked.
+        return !mLauncher.isWorkspaceLocked();
+    }
+
+    @Override
+    public void onPreDragStart(DropTarget.DragObject dragObject) {
+        // The predrag starts when the workspace is not yet loaded. In some cases we set
+        // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
+        // dragView from being visible. Instead just skip the fade-in animation here.
+        mLauncher.getDragLayer().setAlpha(1);
+
+        dragObject.dragView.setColor(
+                mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
+    }
+
+    @Override
+    public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+        if (dragStarted) {
+            dragObject.dragView.setColor(0);
+        }
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        return 1f;
+    }
+
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+            boolean success) {
+        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+            // Exit spring loaded mode if we have not successfully dropped or have not handled the
+            // drop in Workspace
+            mLauncher.exitSpringLoadedDragModeDelayed(true,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        }
+
+        if (!success) {
+            d.deferDragViewCleanupPostAnimation = false;
+        }
+        postCleanup();
+    }
+
+    private void postCleanup() {
+        if (mLauncher != null) {
+            // Remove any drag params from the launcher intent since the drag operation is complete.
+            Intent newIntent = new Intent(mLauncher.getIntent());
+            newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
+            mLauncher.setIntent(newIntent);
+        }
+
+        new Handler(Looper.getMainLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                removeListener();
+            }
+        });
+    }
+
+    public void removeListener() {
+        if (mLauncher != null) {
+            mLauncher.getDragLayer().setOnDragListener(null);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 7410ae6..b852714 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -165,7 +165,7 @@
                 ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                 registrationY, initialDragViewScale, scaleDps);
-
+        dragView.setItemInfo(dragInfo);
         mDragObject.dragComplete = false;
         if (mOptions.isAccessibleDrag) {
             // For an accessible drag, we assume the view is being dragged from the center.
@@ -210,13 +210,13 @@
     }
 
     private void callOnDragStart() {
-        for (DragListener listener : new ArrayList<>(mListeners)) {
-            listener.onDragStart(mDragObject, mOptions);
-        }
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
         mIsInPreDrag = false;
+        for (DragListener listener : new ArrayList<>(mListeners)) {
+            listener.onDragStart(mDragObject, mOptions);
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 65c0f29..d8a3024 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.view.DragEvent;
 import android.view.MotionEvent;
-
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Utilities;
 
@@ -97,16 +96,11 @@
  */
 class SystemDragDriver extends DragDriver {
 
-    private final DragObject mDragObject;
-    private final Context mContext;
-
     float mLastX = 0;
     float mLastY = 0;
 
-    public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
+    SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
         super(dragController);
-        mDragObject = dragObject;
-        mContext = context;
     }
 
     @Override
@@ -162,10 +156,10 @@
  * Class for driving an internal (i.e. not using framework) drag/drop operation.
  */
 class InternalDragDriver extends DragDriver {
-    public InternalDragDriver(DragController dragController) {
+    InternalDragDriver(DragController dragController) {
         super(dragController);
     }
 
     @Override
     public boolean onDragEvent (DragEvent event) { return false; }
-};
+}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 7178c5e..fde7995 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -27,6 +27,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -54,10 +55,12 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.widget.WidgetsBottomSheet;
@@ -72,9 +75,6 @@
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
 
-    // Scrim color without any alpha component.
-    private static final int SCRIM_COLOR = Color.BLACK & 0x00FFFFFF;
-
     private final int[] mTmpXY = new int[2];
 
     @Thunk DragController mDragController;
@@ -107,6 +107,7 @@
     // Related to adjacent page hints
     private final Rect mScrollChildPosition = new Rect();
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
+    private final WallpaperColorInfo mWallpaperColorInfo;
 
     // Related to pinch-to-go-to-overview gesture.
     private PinchToOverviewListener mPinchListener = null;
@@ -130,6 +131,7 @@
 
         mIsRtl = Utilities.isRtl(getResources());
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+        mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext());
     }
 
     public void setup(Launcher launcher, DragController dragController,
@@ -241,7 +243,7 @@
             return true;
         }
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && mAllAppsController.onControllerInterceptTouchEvent(ev)) {
+        if (mAllAppsController.onControllerInterceptTouchEvent(ev)) {
             mActiveController = mAllAppsController;
             return true;
         }
@@ -451,7 +453,8 @@
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
-        setBackgroundResource(insets.top == 0 ? 0 : R.drawable.workspace_bg);
+        setBackground(insets.top == 0 ? null
+                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
     }
 
     @Override
@@ -541,7 +544,6 @@
 
     public void clearResizeFrame() {
         if (mCurrentResizeFrame != null) {
-            mCurrentResizeFrame.commitResize();
             removeView(mCurrentResizeFrame);
             mCurrentResizeFrame = null;
         }
@@ -876,7 +878,10 @@
                 getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
                 canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
             }
-            canvas.drawColor((alpha << 24) | SCRIM_COLOR);
+            // for super light wallpaper it needs to be darken for contrast to workspace
+            // for dark wallpapers the text is white so darkening works as well
+            int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
+            canvas.drawColor(ColorUtils.setAlphaComponent(color, alpha));
             canvas.restore();
         }
 
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 230fa2d..9433aad 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.dragndrop;
 
 import android.graphics.Point;
-import android.support.annotation.CallSuper;
-import android.view.View;
 
 import com.android.launcher3.DropTarget;
 
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7806c98..33d4fa6 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -21,31 +21,64 @@
 import android.animation.FloatArrayEvaluator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
+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 android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfo;
 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.R;
+import com.android.launcher3.Utilities;
+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.util.Themes;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.Arrays;
+import java.util.List;
 
 public class DragView extends View {
+    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;
     @Thunk Paint mPaint;
@@ -57,6 +90,7 @@
 
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
+    private final Launcher mLauncher;
     private final DragLayer mDragLayer;
     @Thunk final DragController mDragController;
     private boolean mHasDrawn = false;
@@ -76,6 +110,13 @@
     private int mAnimatedShiftX;
     private int mAnimatedShiftY;
 
+    // Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true}
+    private Drawable mBgSpringDrawable, mFgSpringDrawable;
+    private SpringFloatValue mTranslateX, mTranslateY;
+    private Path mScaledMaskPath;
+    private Drawable mBadge;
+    private ColorMatrixColorFilter mBaseFilter;
+
     /**
      * Construct the drag view.
      * <p>
@@ -89,6 +130,7 @@
     public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
                     final float initialScale, final float finalScaleDps) {
         super(launcher);
+        mLauncher = launcher;
         mDragLayer = launcher.getDragLayer();
         mDragController = launcher.getDragController();
 
@@ -142,10 +184,208 @@
         mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
 
         mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
         setElevation(getResources().getDimension(R.dimen.drag_elevation));
     }
 
+    /**
+     * 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)) {
+            return;
+        }
+        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
+                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
+                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+            return;
+        }
+        // Load the adaptive icon on a background thread and add the view in ui thread.
+        final Looper workerLooper = LauncherModel.getWorkerLooper();
+        new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
+            @Override
+            public void run() {
+                LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
+                Object[] outObj = new Object[1];
+                final Drawable dr = getFullDrawable(info, appState, 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;
+
+                    Rect bounds = new Rect(0, 0, w, h);
+                    bounds.inset(blurMargin, blurMargin);
+                    // Badge is applied after icon normalization so the bounds for badge should not
+                    // be scaled down due to icon normalization.
+                    Rect badgeBounds = new Rect(bounds);
+                    mBadge = getBadge(info, appState, outObj[0]);
+                    mBadge.setBounds(badgeBounds);
+
+                    Utilities.scaleRectAboutCenter(bounds,
+                            IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null));
+                    AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
+
+                    // Shrink very tiny bit so that the clip path is smaller than the original bitmap
+                    // that has anti aliased edges and shadows.
+                    Rect shrunkBounds = new Rect(bounds);
+                    Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
+                    adaptiveIcon.setBounds(shrunkBounds);
+                    final Path mask = adaptiveIcon.getIconMask();
+
+                    mTranslateX = new SpringFloatValue(DragView.this,
+                            w * AdaptiveIconDrawable.getExtraInsetFraction());
+                    mTranslateY = new SpringFloatValue(DragView.this,
+                            h * AdaptiveIconDrawable.getExtraInsetFraction());
+
+                    bounds.inset(
+                            (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+                            (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+                    );
+                    mBgSpringDrawable = adaptiveIcon.getBackground();
+                    if (mBgSpringDrawable == null) {
+                        mBgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+                    }
+                    mBgSpringDrawable.setBounds(bounds);
+                    mFgSpringDrawable = adaptiveIcon.getForeground();
+                    if (mFgSpringDrawable == null) {
+                        mFgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+                    }
+                    mFgSpringDrawable.setBounds(bounds);
+
+                    new Handler(Looper.getMainLooper()).post(new Runnable() {
+                        @Override
+                        public void run() {
+                            // 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(null);
+                                d.setIsDisabled(true);
+                                mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
+                            }
+                            updateColorFilter();
+                        }
+                    });
+                }
+            }});
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private void updateColorFilter() {
+        if (mCurrentFilter == null) {
+            mPaint.setColorFilter(null);
+
+            if (mScaledMaskPath != null) {
+                mBgSpringDrawable.setColorFilter(mBaseFilter);
+                mBgSpringDrawable.setColorFilter(mBaseFilter);
+                mBadge.setColorFilter(mBaseFilter);
+            }
+        } else {
+            ColorMatrixColorFilter currentFilter = new ColorMatrixColorFilter(mCurrentFilter);
+            mPaint.setColorFilter(currentFilter);
+
+            if (mScaledMaskPath != null) {
+                if (mBaseFilter != null) {
+                    mBaseFilter.getColorMatrix(sTempMatrix1);
+                    sTempMatrix2.set(mCurrentFilter);
+                    sTempMatrix1.postConcat(sTempMatrix2);
+
+                    currentFilter = new ColorMatrixColorFilter(sTempMatrix1);
+                }
+
+                mBgSpringDrawable.setColorFilter(currentFilter);
+                mFgSpringDrawable.setColorFilter(currentFilter);
+                mBadge.setColorFilter(currentFilter);
+            }
+        }
+
+        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) {
+            if (info.id == ItemInfo.NO_ID || !(obj instanceof ShortcutInfoCompat)) {
+                // The item is not yet added on home screen.
+                return new FixedSizeEmptyDrawable(iconSize);
+            }
+            ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
+            Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
+
+            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());
+    }
+
     /** Sets the scale of the view over the normal workspace icon size. */
     public void setIntrinsicIconScaleFactor(float scale) {
         mIntrinsicIconScale = scale;
@@ -188,47 +428,36 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
-    }
-
-    // Draws drag shadow for system DND.
-    @SuppressLint("WrongCall")
-    public void drawDragShadow(Canvas canvas) {
-        final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        canvas.scale(getScaleX(), getScaleY());
-        onDraw(canvas);
-        canvas.restoreToCount(saveCount);
-    }
-
-    // Provides drag shadow metrics for system DND.
-    public void provideDragShadowMetrics(Point size, Point touch) {
-        size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY()));
-
-        final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1);
-        final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1);
-        touch.set(
-                mRegistrationX + (int)Math.round(xGrowth / 2),
-                mRegistrationY + (int)Math.round(yGrowth / 2));
-    }
-
-    @Override
     protected void onDraw(Canvas canvas) {
         mHasDrawn = true;
-        boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
-        if (crossFade) {
-            int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
-            mPaint.setAlpha(alpha);
+
+        if (mDrawBitmap) {
+            // Always draw the bitmap to mask anti aliasing due to clipPath
+            boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
+            if (crossFade) {
+                int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
+                mPaint.setAlpha(alpha);
+            }
+            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+            if (crossFade) {
+                mPaint.setAlpha((int) (255 * mCrossFadeProgress));
+                final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+                float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
+                float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
+                canvas.scale(sX, sY);
+                canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
+                canvas.restoreToCount(saveCount);
+            }
         }
-        canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
-        if (crossFade) {
-            mPaint.setAlpha((int) (255 * mCrossFadeProgress));
-            final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
-            float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
-            canvas.scale(sX, sY);
-            canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
-            canvas.restoreToCount(saveCount);
+
+        if (mScaledMaskPath != null) {
+            int cnt = canvas.save();
+            canvas.clipPath(mScaledMaskPath);
+            mBgSpringDrawable.draw(canvas);
+            canvas.translate(mTranslateX.mValue, mTranslateY.mValue);
+            mFgSpringDrawable.draw(canvas);
+            canvas.restoreToCount(cnt);
+            mBadge.draw(canvas);
         }
     }
 
@@ -265,8 +494,7 @@
             animateFilterTo(m1.getArray());
         } else {
             if (mCurrentFilter == null) {
-                mPaint.setColorFilter(null);
-                invalidate();
+                updateColorFilter();
             } else {
                 animateFilterTo(new ColorMatrix().getArray());
             }
@@ -287,8 +515,7 @@
 
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
-                mPaint.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
-                invalidate();
+                updateColorFilter();
             }
         });
         mFilterAnimator.start();
@@ -343,6 +570,11 @@
      * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
     public void move(int touchX, int touchY) {
+        if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0
+                && mScaledMaskPath != null) {
+            mTranslateX.animateToPos(mLastTouchX - touchX);
+            mTranslateY.animateToPos(mLastTouchY - touchY);
+        }
         mLastTouchX = touchX;
         mLastTouchY = touchY;
         applyTranslation();
@@ -391,4 +623,66 @@
     public float getInitialScale() {
         return mInitialScale;
     }
+
+    private static class SpringFloatValue {
+
+        private static final FloatPropertyCompat<SpringFloatValue> VALUE =
+                new FloatPropertyCompat<SpringFloatValue>("value") {
+                    @Override
+                    public float getValue(SpringFloatValue object) {
+                        return object.mValue;
+                    }
+
+                    @Override
+                    public void setValue(SpringFloatValue object, float value) {
+                        object.mValue = value;
+                        object.mView.invalidate();
+                    }
+                };
+
+        // Following three values are fine tuned with motion ux designer
+        private final static int STIFFNESS = 4000;
+        private final static float DAMPENING_RATIO = 1f;
+        private final static int PARALLAX_MAX_IN_DP = 8;
+
+        private final View mView;
+        private final SpringAnimation mSpring;
+        private final float mDelta;
+
+        private float mValue;
+
+        public SpringFloatValue(View view, float range) {
+            mView = view;
+            mSpring = new SpringAnimation(this, VALUE, 0)
+                    .setMinValue(-range).setMaxValue(range)
+                    .setSpring(new SpringForce(0)
+                            .setDampingRatio(DAMPENING_RATIO)
+                            .setStiffness(STIFFNESS));
+            mDelta = view.getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP;
+        }
+
+        public void animateToPos(float value) {
+            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/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
new file mode 100644
index 0000000..c905460
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -0,0 +1,175 @@
+/*
+ * 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.dragndrop;
+
+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;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.util.Preconditions;
+
+import java.util.concurrent.Callable;
+
+/**
+ * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
+    private static final String TAG = "FolderAdaptiveIcon";
+
+    private final Drawable mBadge;
+    private final Path mMask;
+
+    private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
+        super(bg, fg);
+        mBadge = badge;
+        mMask = mask;
+    }
+
+    @Override
+    public Path getIconMask() {
+        return mMask;
+    }
+
+    public Drawable getBadge() {
+        return mBadge;
+    }
+
+    public static FolderAdaptiveIcon createFolderAdaptiveIcon(
+            final Launcher launcher, final long folderId, Point dragViewSize) {
+        Preconditions.assertNonUiThread();
+        int margin = launcher.getResources()
+                .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
+
+        // Allocate various bitmaps on the background thread, because why not!
+        final Bitmap badge = Bitmap.createBitmap(
+                dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
+
+        // The bitmap for the preview is generated larger than needed to allow for the spring effect
+        float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
+        final Bitmap preview = Bitmap.createBitmap(
+                (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
+                Bitmap.Config.ARGB_8888);
+
+        // Create the actual drawable on the UI thread to avoid race conditions with
+        // FolderIcon draw pass
+        try {
+            return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
+                @Override
+                public FolderAdaptiveIcon call() throws Exception {
+                    FolderIcon icon = launcher.findFolderIcon(folderId);
+                    return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
+                }
+            }).get();
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to create folder icon", e);
+            return null;
+        }
+    }
+
+    /**
+     * Initializes various bitmaps on the UI thread and returns the final drawable.
+     */
+    private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
+            Bitmap badgeBitmap, Bitmap previewBitmap) {
+        Preconditions.assertUIThread();
+        float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
+
+        Canvas c = new Canvas();
+        PreviewBackground bg = icon.getFolderBackground();
+
+        // Initialize badge
+        c.setBitmap(badgeBitmap);
+        bg.drawShadow(c);
+        bg.drawBackgroundStroke(c);
+        icon.drawBadge(c);
+
+        // Initialize preview
+        float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
+                (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
+        float previewShiftX = shiftFactor * previewBitmap.getWidth();
+        float previewShiftY = shiftFactor * previewBitmap.getHeight();
+
+        c.setBitmap(previewBitmap);
+        c.translate(previewShiftX, previewShiftY);
+        icon.getPreviewItemManager().draw(c);
+        c.setBitmap(null);
+
+        // Initialize mask
+        Path mask = new Path();
+        Matrix m = new Matrix();
+        m.setTranslate(margin, margin);
+        bg.getClipPath().transform(m, mask);
+
+        ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
+        ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
+                margin - previewShiftX, margin - previewShiftY);
+
+        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 {
+
+        private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+        private final Bitmap mBitmap;
+        private final float mShiftX;
+        private final float mShiftY;
+
+        ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
+            mBitmap = bitmap;
+            mShiftX = shiftX;
+            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;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
index 36a0292..e36f607 100644
--- a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
+++ b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
@@ -4,7 +4,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.RemoteViews;
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index df0c47c..b9d97ac 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,87 +16,51 @@
 
 package com.android.launcher3.dragndrop;
 
+import android.annotation.TargetApi;
 import android.appwidget.AppWidgetManager;
-import android.content.ClipDescription;
 import android.content.Intent;
-import android.graphics.Point;
+import android.content.pm.LauncherApps.PinItemRequest;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemClock;
-import android.util.Log;
 import android.view.DragEvent;
 import android.view.View;
 import android.widget.RemoteViews;
 
-import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.R;
-import com.android.launcher3.compat.PinItemRequestCompat;
-import com.android.launcher3.folder.Folder;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingItemDragHelper;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
-import java.util.UUID;
-
 /**
  * {@link DragSource} for handling drop from a different window. This object is initialized
  * in the source window and is passed on to the Launcher activity as an Intent extra.
  */
-public class PinItemDragListener
-        implements Parcelable, View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+@TargetApi(Build.VERSION_CODES.O)
+public class PinItemDragListener extends BaseItemDragListener implements Parcelable {
 
-    private static final String TAG = "PinItemDragListener";
-
-    private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
     public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
 
-    private final PinItemRequestCompat mRequest;
+    private final PinItemRequest mRequest;
 
-    // Position of preview relative to the touch location
-    private final Rect mPreviewRect;
-
-    private final int mPreviewBitmapWidth;
-    private final int mPreviewViewWidth;
-
-    // Randomly generated id used to verify the drag event.
-    private final String mId;
-
-    private Launcher mLauncher;
-    private DragController mDragController;
-    private long mDragStartTime;
-
-    public PinItemDragListener(PinItemRequestCompat request, Rect previewRect,
+    public PinItemDragListener(PinItemRequest request, Rect previewRect,
             int previewBitmapWidth, int previewViewWidth) {
+        super(previewRect, previewBitmapWidth, previewViewWidth);
         mRequest = request;
-        mPreviewRect = previewRect;
-        mPreviewBitmapWidth = previewBitmapWidth;
-        mPreviewViewWidth = previewViewWidth;
-        mId = UUID.randomUUID().toString();
     }
 
     private PinItemDragListener(Parcel parcel) {
-        mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
-        mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
-        mPreviewBitmapWidth = parcel.readInt();
-        mPreviewViewWidth = parcel.readInt();
-        mId = parcel.readString();
-    }
-
-    public String getMimeType() {
-        return MIME_TYPE_PREFIX + mId;
+        super(parcel);
+        mRequest = PinItemRequest.CREATOR.createFromParcel(parcel);
     }
 
     @Override
@@ -106,47 +70,22 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int i) {
+        super.writeToParcel(parcel, i);
         mRequest.writeToParcel(parcel, i);
-        mPreviewRect.writeToParcel(parcel, i);
-        parcel.writeInt(mPreviewBitmapWidth);
-        parcel.writeInt(mPreviewViewWidth);
-        parcel.writeString(mId);
-    }
-
-    public void setLauncher(Launcher launcher) {
-        mLauncher = launcher;
-        mDragController = launcher.getDragController();
     }
 
     @Override
-    public boolean onDrag(View view, DragEvent event) {
-        if (mLauncher == null || mDragController == null) {
-            postCleanup();
-            return false;
-        }
-        if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
-            if (onDragStart(event)) {
-                return true;
-            } else {
-                postCleanup();
-                return false;
-            }
-        }
-        return mDragController.onDragEvent(mDragStartTime, event);
-    }
-
-    private boolean onDragStart(DragEvent event) {
+    protected boolean onDragStart(DragEvent event) {
         if (!mRequest.isValid()) {
             return false;
         }
-        ClipDescription desc =  event.getClipDescription();
-        if (desc == null || !desc.hasMimeType(getMimeType())) {
-            Log.e(TAG, "Someone started a dragAndDrop before us.");
-            return false;
-        }
+        return super.onDragStart(event);
+    }
 
+    @Override
+    protected PendingItemDragHelper createDragHelper() {
         final PendingAddItemInfo item;
-        if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
             item = new PendingAddShortcutInfo(
                     new PinShortcutRequestActivityInfo(mRequest, mLauncher));
         } else {
@@ -166,112 +105,20 @@
         View view = new View(mLauncher);
         view.setTag(item);
 
-        Point downPos = new Point((int) event.getX(), (int) event.getY());
-        DragOptions options = new DragOptions();
-        options.systemDndStartPoint = downPos;
-        options.preDragCondition = this;
-
-        // 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,
-        // and the absolute position (position relative to the screen) of drag event is same
-        // across windows, using drag position here give a good estimate for relative position
-        // to source window.
         PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
-        if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET) {
+        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
             dragHelper.setPreview(getPreview(mRequest));
         }
-
-        dragHelper.startDrag(new Rect(mPreviewRect),
-                mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
-        mDragStartTime = SystemClock.uptimeMillis();
-        return true;
-    }
-
-    @Override
-    public boolean shouldStartDrag(double distanceDragged) {
-        // Stay in pre-drag mode, if workspace is locked.
-        return !mLauncher.isWorkspaceLocked();
-    }
-
-    @Override
-    public void onPreDragStart(DropTarget.DragObject dragObject) {
-        // The predrag starts when the workspace is not yet loaded. In some cases we set
-        // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
-        // dragView from being visible. Instead just skip the fade-in animation here.
-        mLauncher.getDragLayer().setAlpha(1);
-
-        dragObject.dragView.setColor(
-                mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
-    }
-
-    @Override
-    public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
-        if (dragStarted) {
-            dragObject.dragView.setColor(0);
-        }
-    }
-
-    @Override
-    public boolean supportsAppInfoDropTarget() {
-        return false;
-    }
-
-    @Override
-    public boolean supportsDeleteDropTarget() {
-        return false;
-    }
-
-    @Override
-    public float getIntrinsicIconScaleFactor() {
-        return 1f;
-    }
-
-    @Override
-    public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
-            boolean success) {
-        if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
-                !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
-            // Exit spring loaded mode if we have not successfully dropped or have not handled the
-            // drop in Workspace
-            mLauncher.exitSpringLoadedDragModeDelayed(true,
-                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
-        }
-
-        if (!success) {
-            d.deferDragViewCleanupPostAnimation = false;
-        }
-        postCleanup();
+        return dragHelper;
     }
 
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
             LauncherLogProto.Target targetParent) {
-        targetParent.containerType = ContainerType.PINITEM;
+        targetParent.containerType = LauncherLogProto.ContainerType.PINITEM;
     }
 
-    private void postCleanup() {
-        if (mLauncher != null) {
-            // Remove any drag params from the launcher intent since the drag operation is complete.
-            Intent newIntent = new Intent(mLauncher.getIntent());
-            newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
-            mLauncher.setIntent(newIntent);
-        }
-
-        new Handler(Looper.getMainLooper()).post(new Runnable() {
-            @Override
-            public void run() {
-                removeListener();
-            }
-        });
-    }
-
-    public void removeListener() {
-        if (mLauncher != null) {
-            mLauncher.getDragLayer().setOnDragListener(null);
-        }
-    }
-
-    public static RemoteViews getPreview(PinItemRequestCompat request) {
+    public static RemoteViews getPreview(PinItemRequest request) {
         Bundle extras = request.getExtras();
         if (extras != null &&
                 extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW) instanceof RemoteViews) {
@@ -281,6 +128,9 @@
     }
 
     public static boolean handleDragRequest(Launcher launcher, Intent intent) {
+        if (!Utilities.ATLEAST_OREO) {
+            return false;
+        }
         if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) {
             return false;
         }
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index bb5ac5b..a70a9bb 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -21,35 +21,37 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
 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.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 
 /**
  * Extension of ShortcutConfigActivityInfo to be used in the confirmation prompt for pin item
  * request.
  */
-@TargetApi(Build.VERSION_CODES.N_MR1)
+@TargetApi(Build.VERSION_CODES.O)
 class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
 
     // Class name used in the target component, such that it will never represent an
     // actual existing class.
     private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
 
-    private final PinItemRequestCompat mRequest;
+    private final PinItemRequest mRequest;
     private final ShortcutInfo mInfo;
     private final Context mContext;
 
-    public PinShortcutRequestActivityInfo(PinItemRequestCompat request, Context context) {
+    public PinShortcutRequestActivityInfo(PinItemRequest request, Context context) {
         super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
                 request.getShortcutInfo().getUserHandle());
         mRequest = request;
@@ -69,8 +71,12 @@
 
     @Override
     public Drawable getFullResIcon(IconCache cache) {
-        return mContext.getSystemService(LauncherApps.class)
+        Drawable d = mContext.getSystemService(LauncherApps.class)
                 .getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
+        if (d == null) {
+            d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle()));
+        }
+        return d;
     }
 
     @Override
@@ -80,7 +86,7 @@
                 Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT +
                 mContext.getResources().getInteger(R.integer.config_overlayTransitionTime) / 2;
         // Delay the actual accept() call until the drop animation is complete.
-        return LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
+        return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
                 mContext, mRequest, duration);
     }
 
diff --git a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
index b6da6ad..9f617e4 100644
--- a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
+++ b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
@@ -16,15 +16,17 @@
 
 package com.android.launcher3.dragndrop;
 
+import android.annotation.TargetApi;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.compat.PinItemRequestCompat;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
 /**
@@ -33,18 +35,19 @@
  * No config activity is shown even if it is defined in widget config. And a callback is sent when
  * the widget is bound.
  */
+@TargetApi(Build.VERSION_CODES.O)
 public class PinWidgetFlowHandler extends WidgetAddFlowHandler implements Parcelable {
 
-    private final PinItemRequestCompat mRequest;
+    private final PinItemRequest mRequest;
 
-    public PinWidgetFlowHandler(AppWidgetProviderInfo providerInfo, PinItemRequestCompat request) {
+    public PinWidgetFlowHandler(AppWidgetProviderInfo providerInfo, PinItemRequest request) {
         super(providerInfo);
         mRequest = request;
     }
 
     protected PinWidgetFlowHandler(Parcel parcel) {
         super(parcel);
-        mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
+        mRequest = PinItemRequest.CREATOR.createFromParcel(parcel);
     }
 
     @Override
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
new file mode 100644
index 0000000..baedf90
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -0,0 +1,802 @@
+/*
+ * 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.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 com.android.launcher3.compat.WallpaperColorsCompat;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 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
+    private static final float FIT_WEIGHT_H = 1.0f;
+    private static final float FIT_WEIGHT_S = 1.0f;
+    private static final float FIT_WEIGHT_L = 10.0f;
+
+    public static final int MAIN_COLOR_LIGHT = 0xffb0b0b0;
+    public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e;
+    public static final int MAIN_COLOR_DARK = 0xff212121;
+    public static final int SECONDARY_COLOR_DARK = 0xff000000;
+
+    // Temporary variable to avoid allocations
+    private float[] mTmpHSL = new float[3];
+
+    public Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
+        if (inWallpaperColors == null) {
+            return applyFallback(inWallpaperColors);
+        }
+
+        final List<Integer> mainColors = getMainColors(inWallpaperColors);
+        final int mainColorsSize = mainColors.size();
+        final boolean supportsDarkText = (inWallpaperColors.getColorHints() &
+                WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) != 0;
+
+        if (mainColorsSize == 0) {
+            return applyFallback(inWallpaperColors);
+        }
+        // Tonal is not really a sort, it takes a color from the extracted
+        // palette and finds a best fit amongst a collection of pre-defined
+        // palettes. The best fit is tweaked to be closer to the source color
+        // and replaces the original palette
+
+        // Get the most preeminent, non-blacklisted color.
+        Integer bestColor = 0;
+        final float[] hsl = new float[3];
+        for (int i = 0; i < mainColorsSize; i++) {
+            final int colorValue = mainColors.get(i);
+            ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
+                    Color.blue(colorValue), hsl);
+
+            // Stop when we find a color that meets our criteria
+            if (!isBlacklisted(hsl)) {
+                bestColor = colorValue;
+                break;
+            }
+        }
+
+        // Fail if not found
+        if (bestColor == null) {
+            return applyFallback(inWallpaperColors);
+        }
+
+        int colorValue = bestColor;
+        ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
+                hsl);
+
+        // The Android HSL definition requires the hue to go from 0 to 360 but
+        // the Material Tonal Palette defines hues from 0 to 1.
+        hsl[0] /= 360f;
+
+        // Find the palette that contains the closest color
+        TonalPalette palette = findTonalPalette(hsl[0], hsl[1]);
+        if (palette == null) {
+            Log.w(TAG, "Could not find a tonal palette!");
+            return applyFallback(inWallpaperColors);
+        }
+
+        // Figure out what's the main color index in the optimal palette
+        int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
+        if (fitIndex == -1) {
+            Log.w(TAG, "Could not find best fit!");
+            return applyFallback(inWallpaperColors);
+        }
+
+        // Generate the 10 colors palette by offsetting each one of them
+        float[] h = fit(palette.h, hsl[0], fitIndex,
+                Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+        float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
+        float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
+
+        int primaryIndex = fitIndex;
+        int mainColor = getColorInt(primaryIndex, h, s, l);
+
+        // We might want use the fallback in case the extracted color is brighter than our
+        // light fallback or darker than our dark fallback.
+        ColorUtils.colorToHSL(mainColor, mTmpHSL);
+        final float mainLuminosity = mTmpHSL[2];
+        ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
+        final float lightLuminosity = mTmpHSL[2];
+        if (mainLuminosity > lightLuminosity) {
+            return applyFallback(inWallpaperColors);
+        }
+        ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
+        final float darkLuminosity = mTmpHSL[2];
+        if (mainLuminosity < darkLuminosity) {
+            return applyFallback(inWallpaperColors);
+        }
+
+        // Dark colors:
+        // Stops at 4th color, only lighter if dark text is supported
+        if (supportsDarkText) {
+            primaryIndex = h.length - 1;
+        } else if (fitIndex < 2) {
+            primaryIndex = 0;
+        } else {
+            primaryIndex = Math.min(fitIndex, 3);
+        }
+        int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+        int secondaryColor = getColorInt(secondaryIndex, h, s, l);
+
+        return new Pair<>(mainColor, secondaryColor);
+    }
+
+    public static Pair<Integer, Integer> applyFallback(WallpaperColorsCompat inWallpaperColors) {
+        boolean light = inWallpaperColors != null
+                && (inWallpaperColors.getColorHints()
+                    & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT)!= 0;
+        int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
+        int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK;
+        return new Pair<>(innerColor, outerColor);
+    }
+
+    private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
+        mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
+        mTmpHSL[1] = s[fitIndex];
+        mTmpHSL[2] = l[fitIndex];
+        return ColorUtils.HSLToColor(mTmpHSL);
+    }
+
+    /**
+     * Checks if a given color exists in the blacklist
+     * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
+     * @return true if color should be avoided
+     */
+    private boolean isBlacklisted(float[] hsl) {
+        for (ColorRange badRange: BLACKLISTED_COLORS) {
+            if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Offsets all colors by a delta, clamping values that go beyond what's
+     * supported on the color space.
+     * @param data what you want to fit
+     * @param v how big should be the offset
+     * @param index which index to calculate the delta against
+     * @param min minimum accepted value (clamp)
+     * @param max maximum accepted value (clamp)
+     * @return new shifted palette
+     */
+    private static float[] fit(float[] data, float v, int index, float min, float max) {
+        float[] fitData = new float[data.length];
+        float delta = v - data[index];
+
+        for (int i = 0; i < data.length; i++) {
+            fitData[i] = Utilities.boundToRange(data[i] + delta, min, max);
+        }
+
+        return fitData;
+    }
+
+    /**
+     * Finds the closest color in a palette, given another HSL color
+     *
+     * @param palette where to search
+     * @param h hue
+     * @param s saturation
+     * @param l lightness
+     * @return closest index or -1 if palette is empty.
+     */
+    private static int bestFit(@NonNull TonalPalette palette, float h, float s, float l) {
+        int minErrorIndex = -1;
+        float minError = Float.POSITIVE_INFINITY;
+
+        for (int i = 0; i < palette.h.length; i++) {
+            float error =
+                    FIT_WEIGHT_H * Math.abs(h - palette.h[i])
+                            + FIT_WEIGHT_S * Math.abs(s - palette.s[i])
+                            + FIT_WEIGHT_L * Math.abs(l - palette.l[i]);
+            if (error < minError) {
+                minError = error;
+                minErrorIndex = i;
+            }
+        }
+
+        return minErrorIndex;
+    }
+
+    @Nullable
+    private static TonalPalette findTonalPalette(float h, float s) {
+        // Fallback to a grey palette if the color is too desaturated.
+        // This avoids hue shifts.
+        if (s < 0.05f) {
+            return GREY_PALETTE;
+        }
+
+        TonalPalette best = null;
+        float error = Float.POSITIVE_INFINITY;
+
+        for (int i = 0; i < TONAL_PALETTES.length; i++) {
+            final TonalPalette candidate = TONAL_PALETTES[i];
+
+            if (h >= candidate.minHue && h <= candidate.maxHue) {
+                best = candidate;
+                break;
+            }
+
+            if (candidate.maxHue > 1.0f && h >= 0.0f && h <= fract(candidate.maxHue)) {
+                best = candidate;
+                break;
+            }
+
+            if (candidate.minHue < 0.0f && h >= fract(candidate.minHue) && h <= 1.0f) {
+                best = candidate;
+                break;
+            }
+
+            if (h <= candidate.minHue && candidate.minHue - h < error) {
+                best = candidate;
+                error = candidate.minHue - h;
+            } else if (h >= candidate.maxHue && h - candidate.maxHue < error) {
+                best = candidate;
+                error = h - candidate.maxHue;
+            } else if (candidate.maxHue > 1.0f && h >= fract(candidate.maxHue)
+                    && h - fract(candidate.maxHue) < error) {
+                best = candidate;
+                error = h - fract(candidate.maxHue);
+            } else if (candidate.minHue < 0.0f && h <= fract(candidate.minHue)
+                    && fract(candidate.minHue) - h < error) {
+                best = candidate;
+                error = fract(candidate.minHue) - h;
+            }
+        }
+
+        return best;
+    }
+
+    private static float fract(float v) {
+        return v - (float) Math.floor(v);
+    }
+
+    static class TonalPalette {
+        final float[] h;
+        final float[] s;
+        final float[] l;
+        final float minHue;
+        final float maxHue;
+
+        TonalPalette(float[] h, float[] s, float[] l) {
+            if (h.length != s.length || s.length != l.length) {
+                throw new IllegalArgumentException("All arrays should have the same size. h: "
+                        + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
+                        + Arrays.toString(l));
+            }
+
+            this.h = h;
+            this.s = s;
+            this.l = l;
+
+            float minHue = Float.POSITIVE_INFINITY;
+            float maxHue = Float.NEGATIVE_INFINITY;
+
+            for (float v : h) {
+                minHue = Math.min(v, minHue);
+                maxHue = Math.max(v, maxHue);
+            }
+
+            this.minHue = minHue;
+            this.maxHue = maxHue;
+        }
+    }
+
+    // Data definition of Material Design tonal palettes
+    // When the sort type is set to TONAL, these palettes are used to find
+    // a best fit. Each palette is defined as 22 HSL colors
+    private static final TonalPalette[] TONAL_PALETTES = {
+            new TonalPalette(
+                    new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
+                            0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
+                            0.027397260273972573f, 0.017543859649122865f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
+                            1f},
+                    new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
+                            0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
+                            0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
+                            0.8568627450980393f, 0.9254901960784314f}
+            ),
+            new TonalPalette(
+                    new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
+                            0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
+                            0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
+                            0.5748031496062993f, 0.5582010582010583f},
+                    new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
+                            0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
+                            1f},
+                    new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
+                            0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
+                            0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
+                            0.7509803921568627f, 0.8764705882352941f}
+            ),
+            new TonalPalette(
+                    new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
+                            0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
+                            0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
+                            0.508080808080808f, 0.5f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
+                            1f},
+                    new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
+                            0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
+                            0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
+                            0.6764705882352942f, 0.8f}
+            ),
+            new TonalPalette(
+                    new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
+                            0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
+                            0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
+                            0.4671052631578947f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
+                            1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
+                    new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
+                            0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
+                            0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
+                            0.7509803921568627f, 0.8509803921568627f}
+            ),
+            new TonalPalette(
+                    new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
+                            0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
+                            0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
+                            0.3467741935483871f, 0.3703703703703704f},
+                    new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
+                            0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
+                            0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
+                    new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
+                            0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
+                            0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
+                            0.8784313725490196f, 0.9294117647058824f}
+            ),
+            new TonalPalette(
+                    new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
+                            0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
+                            0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
+                            0.1946778711484594f, 0.18604651162790695f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
+                            0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
+                            0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
+                            0.7666666666666666f, 0.8313725490196078f}
+            ),
+            new TonalPalette(
+                    new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
+                            0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
+                            0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
+                            0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
+                            0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
+                            0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
+                            0.8509803921568627f, 0.9f}
+            ),
+            new TonalPalette(
+                    new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
+                            0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
+                            0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
+                            0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
+                            0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
+            ),
+            new TonalPalette(
+                    new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
+                            0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
+                            0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
+                            0.9754098360655739f, 0.9824561403508771f},
+                    new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
+                            0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
+                            1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
+                            0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
+                            0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
+                            0.9254901960784314f}
+            ),
+            new TonalPalette(
+                    new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
+                            0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
+                            0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
+                            0.7771084337349398f, 0.7747747747747749f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
+                            0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
+                    new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
+                            0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
+                            0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
+                            0.7470588235294118f, 0.8450980392156863f}
+            ),
+            new TonalPalette(
+                    new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
+                            0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
+                            0.009057971014492771f, 0.026748971193415648f,
+                            0.041666666666666616f, 0.05303030303030304f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
+                            0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
+                            0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
+                    new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
+                            0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
+                            0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
+                            0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
+            ),
+            new TonalPalette(
+                    new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
+                            0.7802083333333333f, 0.7844311377245509f, 0.796875f,
+                            0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
+                            0.8562091503267975f, 0.8666666666666667f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
+                            0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
+                            0.9560439560439562f, 1f, 1f},
+                    new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
+                            0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
+                            0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
+                            0.9411764705882353f}
+            ),
+            new TonalPalette(
+                    new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+                            0.6666666666666666f, 0.6666666666666666f},
+                    new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
+                            0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
+                            0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
+                            0.29999999999999966f, 0.5000000000000004f},
+                    new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
+                            0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
+                            0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
+                            0.8823529411764706f, 0.9372549019607843f}
+            ),
+            new TonalPalette(
+                    new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
+                            0.9944812362030905f, 0f, 0f,
+                            0.0047348484848484815f, 0.00316455696202532f, 0f,
+                            0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
+                            0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
+                            0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
+                            0.8181818181818189f},
+                    new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
+                            0.4215686274509804f,
+                            0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
+                            0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
+                            0.892156862745098f, 0.9568627450980391f}
+            ),
+            new TonalPalette(
+                    new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
+                            0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
+                            0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
+                            0.9426229508196722f, 0.9444444444444444f},
+                    new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
+                            0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
+                            0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
+                            0.8000000000000006f},
+                    new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
+                            0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
+                            0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
+                            0.8529411764705882f, 0.9411764705882353f}
+            ),
+            new TonalPalette(
+                    new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
+                            0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
+                            0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
+                            0.7252252252252251f, 0.7333333333333333f},
+                    new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
+                            0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
+                            0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
+                            0.45679012345678977f, 0.4545454545454551f},
+                    new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
+                            0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
+                            0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
+                            0.8411764705882353f, 0.9352941176470588f}
+            ),
+            new TonalPalette(
+                    new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
+                            0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
+                            0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
+                            0.6428571428571429f},
+                    new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
+                            0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
+                            0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
+                    new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
+                            0.40588235294117647f, 0.44705882352941173f,
+                            0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
+                            0.8431372549019608f, 0.9372549019607843f}
+            ),
+            new TonalPalette(
+                    new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
+                            0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
+                            0.484375f, 0.4841269841269842f, 0.48444444444444457f,
+                            0.48518518518518516f, 0.4907407407407408f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
+                            0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
+                    new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
+                            0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
+                            0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
+            ),
+            new TonalPalette(
+                    new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
+                            0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
+                            0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
+                            0.5512820512820514f, 0.5666666666666667f},
+                    new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
+                            0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
+                            0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
+                            0.15662650602409653f, 0.151515151515151f},
+                    new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
+                            0.2627450980392157f,
+                            0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
+                            0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
+                            0.9352941176470588f}
+            ),
+            new TonalPalette(
+                    new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
+                            0.03947368421052631f, 0.04166666666666668f,
+                            0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
+                            0.04444444444444459f, 0.05555555555555529f},
+                    new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
+                            0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
+                            0.15315315315315312f, 0.15189873417721522f,
+                            0.15789473684210534f, 0.15789473684210542f},
+                    new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
+                            0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
+                            0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
+                            0.9254901960784314f}
+            ),
+            new TonalPalette(
+                    new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
+                            0.07254901960784313f, 0.0934640522875817f,
+                            0.10457516339869281f, 0.11699346405228758f,
+                            0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
+                            0.12500000000000003f, 0.12777777777777777f},
+                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+                    new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
+                            0.5f, 0.5784313725490196f,
+                            0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
+                            0.9411764705882353f}
+            )
+    };
+
+    private static final TonalPalette GREY_PALETTE = new TonalPalette(
+            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+            new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
+                    0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
+                    0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
+    );
+
+    @SuppressWarnings("WeakerAccess")
+    static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
+
+            // Red
+            new ColorRange(
+                    new Range<>(0f, 20f) /* H */,
+                    new Range<>(0.7f, 1f) /* S */,
+                    new Range<>(0.21f, 0.79f)) /* L */,
+            new ColorRange(
+                    new Range<>(0f, 20f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.355f, 0.653f)),
+
+            // Red Orange
+            new ColorRange(
+                    new Range<>(20f, 40f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.28f, 0.643f)),
+            new ColorRange(
+                    new Range<>(20f, 40f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.414f, 0.561f)),
+            new ColorRange(
+                    new Range<>(20f, 40f),
+                    new Range<>(0f, 3f),
+                    new Range<>(0.343f, 0.584f)),
+
+            // Orange
+            new ColorRange(
+                    new Range<>(40f, 60f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.173f, 0.349f)),
+            new ColorRange(
+                    new Range<>(40f, 60f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.233f, 0.427f)),
+            new ColorRange(
+                    new Range<>(40f, 60f),
+                    new Range<>(0f, 0.3f),
+                    new Range<>(0.231f, 0.484f)),
+
+            // Yellow 60
+            new ColorRange(
+                    new Range<>(60f, 80f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.488f, 0.737f)),
+            new ColorRange(
+                    new Range<>(60f, 80f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.673f, 0.837f)),
+
+            // Yellow Green 80
+            new ColorRange(
+                    new Range<>(80f, 100f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.469f, 0.61f)),
+
+            // Yellow green 100
+            new ColorRange(
+                    new Range<>(100f, 120f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.388f, 0.612f)),
+            new ColorRange(
+                    new Range<>(100f, 120f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.424f, 0.541f)),
+
+            // Green
+            new ColorRange(
+                    new Range<>(120f, 140f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.375f, 0.52f)),
+            new ColorRange(
+                    new Range<>(120f, 140f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.435f, 0.524f)),
+
+            // Green Blue 140
+            new ColorRange(
+                    new Range<>(140f, 160f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.496f, 0.641f)),
+
+            // Seafoam
+            new ColorRange(
+                    new Range<>(160f, 180f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.496f, 0.567f)),
+
+            // Cyan
+            new ColorRange(
+                    new Range<>(180f, 200f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.52f, 0.729f)),
+
+            // Blue
+            new ColorRange(
+                    new Range<>(220f, 240f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.396f, 0.571f)),
+            new ColorRange(
+                    new Range<>(220f, 240f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.425f, 0.551f)),
+
+            // Blue Purple 240
+            new ColorRange(
+                    new Range<>(240f, 260f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.418f, 0.639f)),
+            new ColorRange(
+                    new Range<>(220f, 240f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.441f, 0.576f)),
+
+            // Blue Purple 260
+            new ColorRange(
+                    new Range<>(260f, 280f),
+                    new Range<>(0.3f, 1f), // Bigger range
+                    new Range<>(0.461f, 0.553f)),
+
+            // Fuchsia
+            new ColorRange(
+                    new Range<>(300f, 320f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.484f, 0.588f)),
+            new ColorRange(
+                    new Range<>(300f, 320f),
+                    new Range<>(0.3f, 0.7f),
+                    new Range<>(0.48f, 0.592f)),
+
+            // Pink
+            new ColorRange(
+                    new Range<>(320f, 340f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.466f, 0.629f)),
+
+            // Soft red
+            new ColorRange(
+                    new Range<>(340f, 360f),
+                    new Range<>(0.7f, 1f),
+                    new Range<>(0.437f, 0.596f))
+    };
+
+    /**
+     * Representation of an HSL color range.
+     * <ul>
+     * <li>hsl[0] is Hue [0 .. 360)</li>
+     * <li>hsl[1] is Saturation [0...1]</li>
+     * <li>hsl[2] is Lightness [0...1]</li>
+     * </ul>
+     */
+    static class ColorRange {
+        private Range<Float> mHue;
+        private Range<Float> mSaturation;
+        private Range<Float> mLightness;
+
+        ColorRange(Range<Float> hue, Range<Float> saturation, Range<Float> lightness) {
+            mHue = hue;
+            mSaturation = saturation;
+            mLightness = lightness;
+        }
+
+        boolean containsColor(float h, float s, float l) {
+            if (!mHue.contains(h)) {
+                return false;
+            } else if (!mSaturation.contains(s)) {
+                return false;
+            } else if (!mLightness.contains(l)) {
+                return false;
+            }
+            return true;
+        }
+
+        float[] getCenter() {
+            return new float[] {
+                    mHue.getLower() + (mHue.getUpper() - mHue.getLower()) / 2f,
+                    mSaturation.getLower() + (mSaturation.getUpper() - mSaturation.getLower()) / 2f,
+                    mLightness.getLower() + (mLightness.getUpper() - mLightness.getLower()) / 2f
+            };
+        }
+
+        @Override
+        public String toString() {
+            return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
+        }
+    }
+
+    private static List<Integer> getMainColors(WallpaperColorsCompat wallpaperColors) {
+        LinkedList<Integer> colors = new LinkedList<>();
+        if (wallpaperColors.getPrimaryColor() != 0) {
+            colors.add(wallpaperColors.getPrimaryColor());
+        }
+        if (wallpaperColors.getSecondaryColor() != 0) {
+            colors.add(wallpaperColors.getSecondaryColor());
+        }
+        if (wallpaperColors.getTertiaryColor() != 0) {
+            colors.add(wallpaperColors.getTertiaryColor());
+        }
+        return colors;
+    }
+}
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
index a2e118e..b9dd3b5 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
@@ -21,6 +21,7 @@
 import android.app.job.JobParameters;
 import android.app.job.JobService;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
@@ -82,6 +83,10 @@
                 if (wallpaperManager.getWallpaperInfo() != null) {
                     // We can't extract colors from live wallpapers; always use the default color.
                     extractedColors.updateHotseatPalette(null);
+
+                    if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+                        extractedColors.updateWallpaperThemePalette(null);
+                    }
                 } else {
                     // We extract colors for the hotseat and status bar separately,
                     // since they only consider part of the wallpaper.
@@ -90,6 +95,10 @@
                     if (FeatureFlags.LIGHT_STATUS_BAR) {
                         extractedColors.updateStatusBarPalette(getStatusBarPalette());
                     }
+
+                    if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+                        extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
+                    }
                 }
 
                 // Save the extracted colors and wallpaper id to LauncherProvider.
@@ -173,4 +182,23 @@
                 .clearFilters()
                 .generate();
     }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    private Palette getWallpaperPalette() {
+        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+        if (Utilities.ATLEAST_NOUGAT) {
+            try (ParcelFileDescriptor fd = wallpaperManager
+                    .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
+                Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
+                if (bitmap != null) {
+                    return Palette.from(bitmap).clearFilters().generate();
+                }
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+            }
+        }
+
+        Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
+        return Palette.from(wallpaper).clearFilters().generate();
+    }
 }
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
index 711508e..2d8bb86 100644
--- a/src/com/android/launcher3/dynamicui/ExtractedColors.java
+++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java
@@ -16,13 +16,19 @@
 
 package com.android.launcher3.dynamicui;
 
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Color;
+import android.support.annotation.Nullable;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v7.graphics.Palette;
 import android.util.Log;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id.
@@ -32,31 +38,56 @@
 
     public static final int DEFAULT_LIGHT = Color.WHITE;
     public static final int DEFAULT_DARK = Color.BLACK;
-    public static final int DEFAULT_COLOR = DEFAULT_LIGHT;
 
     // These color profile indices should NOT be changed, since they are used when saving and
     // loading extracted colors. New colors should always be added at the end.
     public static final int VERSION_INDEX = 0;
     public static final int HOTSEAT_INDEX = 1;
     public static final int STATUS_BAR_INDEX = 2;
-    // public static final int VIBRANT_INDEX = 2;
-    // public static final int VIBRANT_DARK_INDEX = 3;
-    // public static final int VIBRANT_LIGHT_INDEX = 4;
-    // public static final int MUTED_INDEX = 5;
-    // public static final int MUTED_DARK_INDEX = 6;
-    // public static final int MUTED_LIGHT_INDEX = 7;
+    public static final int WALLPAPER_VIBRANT_INDEX = 3;
+    public static final int ALLAPPS_GRADIENT_MAIN_INDEX = 4;
+    public static final int ALLAPPS_GRADIENT_SECONDARY_INDEX = 5;
 
-    public static final int NUM_COLOR_PROFILES = 2;
-    private static final int VERSION = 1;
+    private static final int VERSION;
+    private static final int[] DEFAULT_VALUES;
+
+    static {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            VERSION = 3;
+            DEFAULT_VALUES = new int[] {
+                    VERSION,            // VERSION_INDEX
+                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
+                    DEFAULT_DARK,       // STATUS_BAR_INDEX
+                    0xFFCCCCCC,         // WALLPAPER_VIBRANT_INDEX
+                    0xFF000000,         // ALLAPPS_GRADIENT_MAIN_INDEX
+                    0xFF000000          // ALLAPPS_GRADIENT_SECONDARY_INDEX
+            };
+        } else if (FeatureFlags.QSB_IN_HOTSEAT) {
+            VERSION = 2;
+            DEFAULT_VALUES = new int[] {
+                    VERSION,            // VERSION_INDEX
+                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
+                    DEFAULT_DARK,       // STATUS_BAR_INDEX
+                    0xFFCCCCCC,         // WALLPAPER_VIBRANT_INDEX
+            };
+        } else {
+            VERSION = 1;
+            DEFAULT_VALUES = new int[] {
+                    VERSION,            // VERSION_INDEX
+                    0x40FFFFFF,         // HOTSEAT_INDEX: White with 25% alpha
+                    DEFAULT_DARK,       // STATUS_BAR_INDEX
+            };
+        }
+    }
 
     private static final String COLOR_SEPARATOR = ",";
 
-    private int[] mColors;
+    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+    private final int[] mColors;
 
     public ExtractedColors() {
         // The first entry is reserved for the version number.
-        mColors = new int[NUM_COLOR_PROFILES + 1];
-        mColors[VERSION_INDEX] = VERSION;
+        mColors = Arrays.copyOf(DEFAULT_VALUES, DEFAULT_VALUES.length);
     }
 
     public void setColorAtIndex(int index, int color) {
@@ -79,17 +110,6 @@
     }
 
     /**
-     * Decodes a comma-separated String into {@link #mColors}.
-     */
-    void decodeFromString(String colorsString) {
-        String[] splitColorsString = colorsString.split(COLOR_SEPARATOR);
-        mColors = new int[splitColorsString.length];
-        for (int i = 0; i < mColors.length; i++) {
-            mColors[i] = Integer.parseInt(splitColorsString[i]);
-        }
-    }
-
-    /**
      * Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}.
      * These were saved there in {@link ColorExtractionService}.
      */
@@ -97,19 +117,22 @@
         String encodedString = Utilities.getPrefs(context).getString(
                 ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
 
-        decodeFromString(encodedString);
-
-        if (mColors[VERSION_INDEX] != VERSION) {
+        String[] splitColorsString = encodedString.split(COLOR_SEPARATOR);
+        if (splitColorsString.length == DEFAULT_VALUES.length &&
+                Integer.parseInt(splitColorsString[VERSION_INDEX]) == VERSION) {
+            // Parse and apply the saved values.
+            for (int i = 0; i < mColors.length; i++) {
+                mColors[i] = Integer.parseInt(splitColorsString[i]);
+            }
+        } else {
+            // Leave the values as default values as the saved values may not be compatible.
             ExtractionUtils.startColorExtractionService(context);
         }
     }
 
     /** @param index must be one of the index values defined at the top of this class. */
-    public int getColor(int index, int defaultColor) {
-        if (index > VERSION_INDEX && index < mColors.length) {
-            return mColors[index];
-        }
-        return defaultColor;
+    public int getColor(int index) {
+        return mColors[index];
     }
 
     /**
@@ -125,7 +148,7 @@
         } else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
             hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
         } else {
-            hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.25f * 255));
+            hotseatColor = DEFAULT_VALUES[HOTSEAT_INDEX];
         }
         setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
     }
@@ -134,4 +157,28 @@
         setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ?
                 DEFAULT_LIGHT : DEFAULT_DARK);
     }
+
+    public void updateWallpaperThemePalette(@Nullable Palette wallpaperPalette) {
+        int defaultColor = DEFAULT_VALUES[WALLPAPER_VIBRANT_INDEX];
+        setColorAtIndex(WALLPAPER_VIBRANT_INDEX, wallpaperPalette == null
+                ? defaultColor : wallpaperPalette.getVibrantColor(defaultColor));
+    }
+
+    public void addOnChangeListener(OnChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void notifyChange() {
+        for (OnChangeListener listener : mListeners) {
+            listener.onExtractedColorsChanged();
+        }
+    }
+
+    /**
+     * Interface for listening for extracted color changes
+     */
+    public interface OnChangeListener {
+
+        void onExtractedColorsChanged();
+    }
 }
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
index 92cb5dc..cc0e0be 100644
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
@@ -29,6 +29,7 @@
 import android.support.v7.graphics.Palette;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.List;
 
@@ -47,6 +48,9 @@
      * Launcher will be notified in Launcher#onSettingsChanged(String, String).
      */
     public static void startColorExtractionServiceIfNecessary(final Context context) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         // Run on a background thread, since the service is asynchronous anyway.
         Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
@@ -60,6 +64,9 @@
 
     /** Starts the {@link ColorExtractionService} without checking the wallpaper id */
     public static void startColorExtractionService(Context context) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
                 Context.JOB_SCHEDULER_SERVICE);
         jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
new file mode 100644
index 0000000..80a89e3
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -0,0 +1,120 @@
+package com.android.launcher3.dynamicui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Pair;
+
+import com.android.launcher3.compat.WallpaperColorsCompat;
+import com.android.launcher3.compat.WallpaperManagerCompat;
+
+import java.util.ArrayList;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChangedListenerCompat {
+
+    private static final int FALLBACK_COLOR = Color.WHITE;
+    private static final Object sInstanceLock = new Object();
+    private static WallpaperColorInfo sInstance;
+
+    public static WallpaperColorInfo getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new WallpaperColorInfo(context.getApplicationContext());
+            }
+            return sInstance;
+        }
+    }
+
+    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+    private final WallpaperManagerCompat mWallpaperManager;
+    private final ColorExtractionAlgorithm mExtractionType;
+    private int mMainColor;
+    private int mSecondaryColor;
+    private boolean mIsDark;
+    private boolean mSupportsDarkText;
+    private OnThemeChangeListener mOnThemeChangeListener;
+
+    private WallpaperColorInfo(Context context) {
+        mWallpaperManager = WallpaperManagerCompat.getInstance(context);
+        mWallpaperManager.addOnColorsChangedListener(this);
+        mExtractionType = ColorExtractionAlgorithm.newInstance(context);
+        update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
+    }
+
+    public int getMainColor() {
+        return mMainColor;
+    }
+
+    public int getSecondaryColor() {
+        return mSecondaryColor;
+    }
+
+    public boolean isDark() {
+        return mIsDark;
+    }
+
+    public boolean supportsDarkText() {
+        return mSupportsDarkText;
+    }
+
+    @Override
+    public void onColorsChanged(WallpaperColorsCompat colors, int which) {
+        if ((which & FLAG_SYSTEM) != 0) {
+            boolean wasDarkTheme = mIsDark;
+            boolean didSupportDarkText = mSupportsDarkText;
+            update(colors);
+            notifyChange(wasDarkTheme != mIsDark || didSupportDarkText != mSupportsDarkText);
+        }
+    }
+
+    private void update(WallpaperColorsCompat wallpaperColors) {
+        Pair<Integer, Integer> colors = mExtractionType.extractInto(wallpaperColors);
+        if (colors != null) {
+            mMainColor = colors.first;
+            mSecondaryColor = colors.second;
+        } else {
+            mMainColor = FALLBACK_COLOR;
+            mSecondaryColor = FALLBACK_COLOR;
+        }
+        mSupportsDarkText = wallpaperColors != null
+                ? (wallpaperColors.getColorHints()
+                    & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) > 0 : false;
+        mIsDark = wallpaperColors != null
+                ? (wallpaperColors.getColorHints()
+                    & WallpaperColorsCompat.HINT_SUPPORTS_DARK_THEME) > 0 : false;
+    }
+
+    public void setOnThemeChangeListener(OnThemeChangeListener onThemeChangeListener) {
+        this.mOnThemeChangeListener = onThemeChangeListener;
+    }
+
+    public void addOnChangeListener(OnChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeOnChangeListener(OnChangeListener listener) {
+        mListeners.remove(listener);
+    }
+
+    public void notifyChange(boolean themeChanged) {
+        if (themeChanged) {
+            if (mOnThemeChangeListener != null) {
+                mOnThemeChangeListener.onThemeChanged();
+            }
+        } else {
+            for (OnChangeListener listener : mListeners) {
+                listener.onExtractedColorsChanged(this);
+            }
+        }
+    }
+
+    public interface OnChangeListener {
+        void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
+    }
+
+    public interface OnThemeChangeListener {
+        void onThemeChanged();
+    }
+}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 840fcf5..f25345e 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -1,22 +1,18 @@
 package com.android.launcher3.folder;
 
-import android.view.View;
-
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.ArrayList;
-import java.util.List;
 
 public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
 
     static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
     private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
-    private static final int MAX_NUM_ITEMS_PER_ROW = 2;
 
-    final float MIN_SCALE = 0.48f;
-    final float MAX_SCALE = 0.58f;
-    final float MAX_RADIUS_DILATION = 0.15f;
-    final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
+    private static final float MIN_SCALE = 0.48f;
+    private static final float MAX_SCALE = 0.58f;
+    private static final float MAX_RADIUS_DILATION = 0.15f;
+    private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
+
+    private static final int EXIT_INDEX = -2;
+    private static final int ENTER_INDEX = -3;
 
     private float[] mTmpPoint = new float[2];
 
@@ -27,7 +23,7 @@
     private float mBaselineIconScale;
 
     @Override
-    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+    public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
         mAvailableSpace = availableSpace;
         mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
         mIconSize = intrinsicIconSize;
@@ -36,25 +32,33 @@
     }
 
     @Override
-    public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
-            int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
-
+    public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
         float totalScale = scaleForItem(index, curNumItems);
         float transX;
         float transY;
         float overlayAlpha = 0;
 
-        // Items beyond those displayed in the preview are animated to the center
-        if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
-            transX = transY = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
+        if (index == getExitIndex()) {
+            // 0 1 * <-- Exit position (row 0, col 2)
+            // 2 3
+            getGridPosition(0, 2, mTmpPoint);
+        } else if (index == getEnterIndex()) {
+            // 0 1
+            // 2 3 * <-- Enter position (row 1, col 2)
+            getGridPosition(1, 2, mTmpPoint);
+        } else if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
+            // Items beyond those displayed in the preview are animated to the center
+            mTmpPoint[0] = mTmpPoint[1] = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
         } else {
             getPosition(index, curNumItems, mTmpPoint);
-            transX = mTmpPoint[0];
-            transY = mTmpPoint[1];
         }
 
+        transX = mTmpPoint[0];
+        transY = mTmpPoint[1];
+
         if (params == null) {
-            params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
         } else {
             params.update(transX, transY, totalScale);
             params.overlayAlpha = overlayAlpha;
@@ -62,6 +66,27 @@
         return params;
     }
 
+    /**
+     * Builds a grid based on the positioning of the items when there are
+     * {@link #MAX_NUM_ITEMS_IN_PREVIEW} in the preview.
+     *
+     * Positions in the grid: 0 1  // 0 is row 0, col 1
+     *                        2 3  // 3 is row 1, col 1
+     */
+    private void getGridPosition(int row, int col, float[] result) {
+        // We use position 0 and 3 to calculate the x and y distances between items.
+        getPosition(0, 4, result);
+        float left = result[0];
+        float top = result[1];
+
+        getPosition(3, 4, result);
+        float dx = result[0] - left;
+        float dy = result[1] - top;
+
+        result[0] = left + (col * dx);
+        result[1] = top + (row * dy);
+    }
+
     private void getPosition(int index, int curNumItems, float[] result) {
         // The case of two items is homomorphic to the case of one.
         curNumItems = Math.max(curNumItems, 2);
@@ -121,6 +146,11 @@
     }
 
     @Override
+    public float getIconSize() {
+        return mIconSize;
+    }
+
+    @Override
     public int maxNumItems() {
         return MAX_NUM_ITEMS_IN_PREVIEW;
     }
@@ -131,22 +161,17 @@
     }
 
     @Override
-    public List<View> getItemsToDisplay(Folder folder) {
-        List<View> items = new ArrayList<>(folder.getItemsInReadingOrder());
-        int numItems = items.size();
-        if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION && numItems > MAX_NUM_ITEMS_IN_PREVIEW) {
-            // We match the icons in the preview with the layout of the opened folder (b/27944225),
-            // but we still need to figure out how we want to handle updating the preview when the
-            // upper left quadrant changes.
-            int appsPerRow = folder.mContent.getPageAt(0).getCountX();
-            int appsToDelete = appsPerRow - MAX_NUM_ITEMS_PER_ROW;
+    public boolean hasEnterExitIndices() {
+        return true;
+    }
 
-            // We only display the upper left quadrant.
-            while (appsToDelete > 0) {
-                items.remove(MAX_NUM_ITEMS_PER_ROW);
-                appsToDelete--;
-            }
-        }
-        return items.subList(0, Math.min(numItems, MAX_NUM_ITEMS_IN_PREVIEW));
+    @Override
+    public int getExitIndex() {
+        return EXIT_INDEX;
+    }
+
+    @Override
+    public int getEnterIndex() {
+        return ENTER_INDEX;
     }
 }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 93c9ea8..85792d4 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -34,7 +34,6 @@
 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.accessibility.AccessibilityEvent;
@@ -46,6 +45,7 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Alarm;
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
@@ -66,8 +66,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
+import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -75,12 +76,13 @@
 import com.android.launcher3.pageindicators.PageIndicatorDots;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * Represents a set of icons chosen by the user or generated by the system.
@@ -133,8 +135,10 @@
 
     @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
 
+    private AnimatorSet mCurrentAnimator;
+
     private final int mExpandDuration;
-    private final int mMaterialExpandDuration;
+    public final int mMaterialExpandDuration;
     private final int mMaterialExpandStagger;
 
     protected final Launcher mLauncher;
@@ -279,9 +283,6 @@
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
             ShortcutInfo item = (ShortcutInfo) tag;
-            if (!v.isInTouchMode()) {
-                return false;
-            }
 
             mEmptyCellRank = item.rank;
             mCurrentDragView = v;
@@ -331,6 +332,7 @@
         if (mIsExternalDrag && mDragInProgress) {
             completeDragExit();
         }
+        mDragInProgress = false;
         mDragController.removeDragListener(this);
     }
 
@@ -351,13 +353,14 @@
 
     @Override
     public boolean onBackKey() {
-        mFolderName.setHint(sHintText);
         // Convert to a string here to ensure that no other state associated with the text field
         // gets saved.
         String newTitle = mFolderName.getText().toString();
         mInfo.setTitle(newTitle);
         mLauncher.getModelWriter().updateItemInDatabase(mInfo);
 
+        mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null);
+
         Utilities.sendCustomAccessibilityEvent(
                 this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                 getContext().getString(R.string.folder_renamed, newTitle));
@@ -454,8 +457,10 @@
 
         if (!sDefaultFolderName.contentEquals(mInfo.title)) {
             mFolderName.setText(mInfo.title);
+            mFolderName.setHint(null);
         } else {
             mFolderName.setText("");
+            mFolderName.setHint(sHintText);
         }
 
         // In case any children didn't come across during loading, clean up the folder accordingly
@@ -501,51 +506,31 @@
         mState = STATE_SMALL;
     }
 
-    /**
-     * Opens the user folder described by the specified tag. The opening of the folder
-     * is animated relative to the specified View. If the View is null, no animation
-     * is played.
-     */
-    public void animateOpen() {
-        Folder openFolder = getOpen(mLauncher);
-        if (openFolder != null && openFolder != this) {
-            // Close any open folder before opening a folder.
-            openFolder.close(true);
+    private void startAnimation(final AnimatorSet a) {
+        if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.cancel();
         }
-
-        DragLayer dragLayer = mLauncher.getDragLayer();
-        // Just verify that the folder hasn't already been added to the DragLayer.
-        // There was a one-off crash where the folder had a parent already.
-        if (getParent() == null) {
-            dragLayer.addView(this);
-            mDragController.addDropTarget(this);
-        } else {
-            if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
-                        + getParent());
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+                mCurrentAnimator = a;
             }
-        }
 
-        mIsOpen = true;
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+        a.start();
+    }
 
-        mContent.completePendingPageChanges();
-        if (!mDragInProgress) {
-            // Open on the first page.
-            mContent.snapToPageImmediately(0);
-        }
-
-        // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
-        // leads to an inconsistent state if you drag out of the folder and drag back in without
-        // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
-        mDeleteFolderOnDropCompleted = false;
-
-        final Runnable onCompleteRunnable;
+    private AnimatorSet getOpeningAnimator() {
         prepareReveal();
-        centerAboutIcon();
-
         mFolderIcon.growAndFadeOut();
 
         AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+
         int width = getFolderWidth();
         int height = getFolderHeight();
 
@@ -587,24 +572,78 @@
         anim.play(textAlpha);
         anim.play(reveal);
 
-        mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
-        mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
+        AnimationLayerSet layerSet = new AnimationLayerSet();
+        layerSet.addView(mContent);
+        layerSet.addView(mFooter);
+        anim.addListener(layerSet);
+
+        return anim;
+    }
+
+    /**
+     * Opens the user folder described by the specified tag. The opening of the folder
+     * is animated relative to the specified View. If the View is null, no animation
+     * is played.
+     */
+    public void animateOpen() {
+        Folder openFolder = getOpen(mLauncher);
+        if (openFolder != null && openFolder != this) {
+            // Close any open folder before opening a folder.
+            openFolder.close(true);
+        }
+
+        DragLayer dragLayer = mLauncher.getDragLayer();
+        // Just verify that the folder hasn't already been added to the DragLayer.
+        // There was a one-off crash where the folder had a parent already.
+        if (getParent() == null) {
+            dragLayer.addView(this);
+            mDragController.addDropTarget(this);
+        } else {
+            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+                        + getParent());
+            }
+        }
+
+        mIsOpen = true;
+
+        mContent.completePendingPageChanges();
+        if (!mDragInProgress) {
+            // Open on the first page.
+            mContent.snapToPageImmediately(0);
+        }
+
+        // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+        // leads to an inconsistent state if you drag out of the folder and drag back in without
+        // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+        mDeleteFolderOnDropCompleted = false;
+
+        final Runnable onCompleteRunnable;
+        centerAboutIcon();
+
+        AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+                ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
+                : getOpeningAnimator();
         onCompleteRunnable = new Runnable() {
             @Override
             public void run() {
-                mContent.setLayerType(LAYER_TYPE_NONE, null);
-                mFooter.setLayerType(LAYER_TYPE_NONE, null);
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                    mFolderIcon.setBackgroundVisible(false);
+                    mFolderIcon.drawLeaveBehindIfExists();
+                } else {
+                    mFolderIcon.setVisibility(INVISIBLE);
+                }
+
                 Utilities.sendCustomAccessibilityEvent(
                         Folder.this,
                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         mContent.getAccessibilityDescription());
-                mState = STATE_ANIMATING;
             }
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -650,7 +689,7 @@
         }
 
         mPageIndicator.stopAllAnimations();
-        anim.start();
+        startAnimation(anim);
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
@@ -689,7 +728,11 @@
         }
 
         if (mFolderIcon != null) {
-            mFolderIcon.shrinkAndFadeIn(animate);
+            if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                mFolderIcon.clearLeaveBehindIfExists();
+            } else {
+                mFolderIcon.shrinkAndFadeIn(animate);
+            }
         }
 
         if (!(getParent() instanceof DragLayer)) return;
@@ -706,12 +749,24 @@
         parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    private AnimatorSet getClosingAnimator() {
+        AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+        animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
+
+        AnimationLayerSet layerSet = new AnimationLayerSet();
+        layerSet.addView(this);
+        animatorSet.addListener(layerSet);
+        animatorSet.setDuration(mExpandDuration);
+        return animatorSet;
+    }
+
     private void animateClosed() {
-        final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
-        oa.addListener(new AnimatorListenerAdapter() {
+        AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+                ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
+                : getClosingAnimator();
+        a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                setLayerType(LAYER_TYPE_NONE, null);
                 closeComplete(true);
             }
             @Override
@@ -720,12 +775,9 @@
                         Folder.this,
                         AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         getContext().getString(R.string.folder_closed));
-                mState = STATE_ANIMATING;
             }
         });
-        oa.setDuration(mExpandDuration);
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-        oa.start();
+        startAnimation(a);
     }
 
     private void closeComplete(boolean wasAnimated) {
@@ -736,8 +788,23 @@
         }
         mDragController.removeDropTarget(this);
         clearFocus();
-        if (wasAnimated) {
-            mFolderIcon.requestFocus();
+        if (mFolderIcon != null) {
+            mFolderIcon.setVisibility(View.VISIBLE);
+            if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                mFolderIcon.setBackgroundVisible(true);
+                mFolderIcon.mFolderName.setTextVisibility(true);
+            }
+            if (wasAnimated) {
+                if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                    mFolderIcon.mBackground.fadeInBackgroundShadow();
+                    mFolderIcon.mBackground.animateBackgroundStroke();
+                    mFolderIcon.onFolderClose(mContent.getCurrentPage());
+                }
+                if (mFolderIcon.hasBadge()) {
+                    mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
+                }
+                mFolderIcon.requestFocus();
+            }
         }
 
         if (mRearrangeOnClose) {
@@ -754,6 +821,7 @@
         mSuppressFolderDeletion = false;
         clearDragInfo();
         mState = STATE_SMALL;
+        mContent.setCurrentPage(0);
     }
 
     public boolean acceptDrop(DragObject d) {
@@ -1027,6 +1095,9 @@
     }
 
     public boolean isDropEnabled() {
+        if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+            return mState != STATE_ANIMATING;
+        }
         return true;
     }
 
@@ -1213,7 +1284,7 @@
         };
         View finalChild = mContent.getLastItem();
         if (finalChild != null) {
-            mFolderIcon.performDestroyAnimation(finalChild, onCompleteRunnable);
+            mFolderIcon.performDestroyAnimation(onCompleteRunnable);
         } else {
             onCompleteRunnable.run();
         }
@@ -1288,53 +1359,73 @@
         }
         mContent.completePendingPageChanges();
 
-        View currentDragView;
-        final ShortcutInfo si;
-        if (d.dragInfo instanceof AppInfo) {
-            // Came from all apps -- make a copy.
-            si = ((AppInfo) d.dragInfo).makeShortcut();
-        } else {
-            // ShortcutInfo
-            si = (ShortcutInfo) d.dragInfo;
-        }
-        if (mIsExternalDrag) {
-            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.
-            mLauncher.getModelWriter().addOrMoveItemInDatabase(
-                    si, mInfo.id, 0, si.cellX, si.cellY);
+        PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
+                ? (PendingAddShortcutInfo) d.dragInfo : null;
+        ShortcutInfo pasiSi = pasi != null ? pasi.activityInfo.createShortcutInfo() : null;
+        if (pasi != null && pasiSi == null) {
+            // There is no ShortcutInfo, so we have to go through a configuration activity.
+            pasi.container = mInfo.id;
+            pasi.rank = mEmptyCellRank;
 
-            // We only need to update the locations if it doesn't get handled in #onDropCompleted.
-            if (d.dragSource != this) {
-                updateItemLocationsInDatabaseBatch();
-            }
-            mIsExternalDrag = false;
-        } else {
-            currentDragView = mCurrentDragView;
-            mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
-        }
-
-        if (d.dragView.hasDrawn()) {
-
-            // Temporarily reset the scale such that the animation target gets calculated correctly.
-            float scaleX = getScaleX();
-            float scaleY = getScaleY();
-            setScaleX(1.0f);
-            setScaleY(1.0f);
-            mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
-                    cleanUpRunnable, null);
-            setScaleX(scaleX);
-            setScaleY(scaleY);
-        } else {
+            mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+                    pasi.spanY);
             d.deferDragViewCleanupPostAnimation = false;
-            currentDragView.setVisibility(VISIBLE);
-        }
-        mItemsInvalidated = true;
-        rearrangeChildren();
+            mRearrangeOnClose = true;
+        } else {
+            final ShortcutInfo si;
+            if (pasiSi != null) {
+                si = pasiSi;
+            } else if (d.dragInfo instanceof AppInfo) {
+                // Came from all apps -- make a copy.
+                si = ((AppInfo) d.dragInfo).makeShortcut();
+            } else {
+                // ShortcutInfo
+                si = (ShortcutInfo) d.dragInfo;
+            }
 
-        // Temporarily suppress the listener, as we did all the work already here.
-        try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-            mInfo.add(si, false);
+            View currentDragView;
+            if (mIsExternalDrag) {
+                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.
+                mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                        si, mInfo.id, 0, si.cellX, si.cellY);
+
+                // We only need to update the locations if it doesn't get handled in
+                // #onDropCompleted.
+                if (d.dragSource != this) {
+                    updateItemLocationsInDatabaseBatch();
+                }
+                mIsExternalDrag = false;
+            } else {
+                currentDragView = mCurrentDragView;
+                mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+            }
+
+            if (d.dragView.hasDrawn()) {
+                // Temporarily reset the scale such that the animation target gets calculated
+                // correctly.
+                float scaleX = getScaleX();
+                float scaleY = getScaleY();
+                setScaleX(1.0f);
+                setScaleY(1.0f);
+                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
+                        cleanUpRunnable, null);
+                setScaleX(scaleX);
+                setScaleY(scaleY);
+            } else {
+                d.deferDragViewCleanupPostAnimation = false;
+                currentDragView.setVisibility(VISIBLE);
+            }
+
+            mItemsInvalidated = true;
+            rearrangeChildren();
+
+            // Temporarily suppress the listener, as we did all the work already here.
+            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+                mInfo.add(si, false);
+            }
         }
 
         // Clear the drag info, as it is no longer being dragged.
@@ -1363,11 +1454,15 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item) {
-        mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
+    public void onAdd(ShortcutInfo item, int rank) {
+        View view = mContent.createAndAddViewForRank(item, rank);
+        mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
+                item.cellY);
+
+        ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
+        items.add(rank, view);
+        mContent.arrangeChildren(items, items.size());
         mItemsInvalidated = true;
-        mLauncher.getModelWriter().addOrMoveItemInDatabase(
-                item, mInfo.id, 0, item.cellX, item.cellY);
     }
 
     public void onRemove(ShortcutInfo item) {
@@ -1427,6 +1522,25 @@
         return mItemsInReadingOrder;
     }
 
+    public List<BubbleTextView> getItemsOnPage(int page) {
+        ArrayList<View> allItems = getItemsInReadingOrder();
+        int lastPage = mContent.getPageCount() - 1;
+        int totalItemsInFolder = allItems.size();
+        int itemsPerPage = mContent.itemsPerPage();
+        int numItemsOnCurrentPage = page == lastPage
+                ? totalItemsInFolder - (itemsPerPage * page)
+                : itemsPerPage;
+
+        int startIndex = page * itemsPerPage;
+        int endIndex = Math.min(startIndex + numItemsOnCurrentPage, allItems.size());
+
+        List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
+        for (int i = startIndex; i < endIndex; ++i) {
+            itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
+        }
+        return itemsOnCurrentPage;
+    }
+
     public void onFocusChange(View v, boolean hasFocus) {
         if (v == mFolderName) {
             if (hasFocus) {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
new file mode 100644
index 0000000..69705d5
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -0,0 +1,374 @@
+/*
+ * 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.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+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 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.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;
+
+import java.util.List;
+
+/**
+ * Manages the opening and closing animations for a {@link Folder}.
+ *
+ * All of the animations are done in the Folder.
+ * ie. When the user taps on the FolderIcon, we immediately hide the FolderIcon and show the Folder
+ * in its place before starting the animation.
+ */
+public class FolderAnimationManager {
+
+    private Folder mFolder;
+    private FolderPagedView mContent;
+    private GradientDrawable mFolderBackground;
+
+    private FolderIcon mFolderIcon;
+    private PreviewBackground mPreviewBackground;
+
+    private Context mContext;
+    private Launcher mLauncher;
+
+    private final boolean mIsOpening;
+
+    private final int mDuration;
+    private final int mDelay;
+
+    private final TimeInterpolator mFolderInterpolator;
+    private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
+    private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
+
+    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+    private static final Property<View, Float> SCALE_PROPERTY =
+            new Property<View, Float>(Float.class, "scale") {
+                @Override
+                public Float get(View view) {
+                    return view.getScaleX();
+                }
+
+                @Override
+                public void set(View view, Float scale) {
+                    view.setScaleX(scale);
+                    view.setScaleY(scale);
+                }
+            };
+
+    public FolderAnimationManager(Folder folder, boolean isOpening) {
+        mFolder = folder;
+        mContent = folder.mContent;
+        mFolderBackground = (GradientDrawable) mFolder.getBackground();
+
+        mFolderIcon = folder.mFolderIcon;
+        mPreviewBackground = mFolderIcon.mBackground;
+
+        mContext = folder.getContext();
+        mLauncher = folder.mLauncher;
+
+        mIsOpening = isOpening;
+
+        mDuration = mFolder.mMaterialExpandDuration;
+        mDelay = mContext.getResources().getInteger(R.integer.config_folderDelay);
+
+        mFolderInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.folder_interpolator);
+        mLargeFolderPreviewItemOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.large_folder_preview_item_open_interpolator);
+        mLargeFolderPreviewItemCloseInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.large_folder_preview_item_close_interpolator);
+    }
+
+
+    /**
+     * Prepares the Folder for animating between open / closed states.
+     */
+    public AnimatorSet getAnimator() {
+        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+        FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+        final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
+
+        // Match position of the FolderIcon
+        final Rect folderIconPos = new Rect();
+        float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+                .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+        int scaledRadius = mPreviewBackground.getScaledRadius();
+        float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer;
+
+        // Match size/scale of icons in the preview
+        float previewScale = rule.scaleForItem(0, itemsInPreview.size());
+        float previewSize = rule.getIconSize() * previewScale;
+        float initialScale = previewSize / itemsInPreview.get(0).getIconSize()
+                * scaleRelativeToDragLayer;
+        final float finalScale = 1f;
+        float scale = mIsOpening ? initialScale : finalScale;
+        mFolder.setScaleX(scale);
+        mFolder.setScaleY(scale);
+        mFolder.setPivotX(0);
+        mFolder.setPivotY(0);
+
+        // We want to create a small X offset for the preview items, so that they follow their
+        // expected path to their final locations. ie. an icon should not move right, if it's final
+        // location is to its left. This value is arbitrarily defined.
+        int previewItemOffsetX = (int) (previewSize / 2);
+        if (Utilities.isRtl(mContext.getResources())) {
+            previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX);
+        }
+
+        final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
+                * initialScale);
+        final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
+                * initialScale);
+
+        int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX
+                - previewItemOffsetX;
+        int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY;
+        final float xDistance = initialX - lp.x;
+        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());
+        mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
+
+        // Set up the reveal animation that clips the Folder.
+        int totalOffsetX = paddingOffsetX + previewItemOffsetX;
+        Rect startRect = new Rect(
+                Math.round(totalOffsetX / initialScale),
+                Math.round(paddingOffsetY / initialScale),
+                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());
+
+        // Create the animators.
+        AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+
+        // Initialize the Folder items' text.
+        PropertyResetListener colorResetListener = new PropertyResetListener<>(
+                BubbleTextView.TEXT_ALPHA_PROPERTY,
+                Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
+        for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
+            if (mIsOpening) {
+                icon.setTextVisibility(false);
+            }
+            ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening);
+            anim.addListener(colorResetListener);
+            play(a, anim);
+        }
+
+        play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
+        play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
+        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));
+
+        // Animate the elevation midway so that the shadow is not noticeable in the background.
+        int midDuration = mDuration / 2;
+        Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
+        play(a, z, mIsOpening ? midDuration : 0, midDuration);
+
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mFolder.setTranslationX(0.0f);
+                mFolder.setTranslationY(0.0f);
+                mFolder.setTranslationZ(0.0f);
+                mFolder.setScaleX(1f);
+                mFolder.setScaleY(1f);
+            }
+        });
+
+        // We set the interpolator on all current child animators here, because the preview item
+        // animators may use a different interpolator.
+        for (Animator animator : a.getChildAnimations()) {
+            animator.setInterpolator(mFolderInterpolator);
+        }
+
+        int radiusDiff = scaledRadius - mPreviewBackground.getRadius();
+        addPreviewItemAnimators(a, initialScale / scaleRelativeToDragLayer,
+                // Background can have a scaled radius in drag and drop mode, so we need to add the
+                // difference to keep the preview items centered.
+                previewItemOffsetX + radiusDiff, radiusDiff);
+        return a;
+    }
+
+    /**
+     * Animate the items on the current page.
+     */
+    private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
+            int previewItemOffsetX, int previewItemOffsetY) {
+        FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+        boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
+        final List<BubbleTextView> itemsInPreview = isOnFirstPage
+                ? mFolderIcon.getPreviewItems()
+                : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
+        final int numItemsInPreview = itemsInPreview.size();
+        final int numItemsInFirstPagePreview = isOnFirstPage
+                ? numItemsInPreview
+                : FolderIcon.NUM_ITEMS_IN_PREVIEW;
+
+        TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
+
+        ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
+        for (int i = 0; i < numItemsInPreview; ++i) {
+            final BubbleTextView btv = itemsInPreview.get(i);
+            CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+
+            // Calculate the final values in the LayoutParams.
+            btvLp.isLockedToGrid = true;
+            cwc.setupLp(btv);
+
+            // Match scale of icons in the preview of the items on the first page.
+            float previewScale = rule.scaleForItem(i, numItemsInFirstPagePreview);
+            float previewSize = rule.getIconSize() * previewScale;
+            float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
+
+            final float initialScale = iconScale / folderScale;
+            final float finalScale = 1f;
+            float scale = mIsOpening ? initialScale : finalScale;
+            btv.setScaleX(scale);
+            btv.setScaleY(scale);
+
+            // Match positions of the icons in the folder with their positions in the preview
+            rule.computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, mTmpParams);
+            // The PreviewLayoutRule assumes that the icon size takes up the entire width so we
+            // offset by the actual size.
+            int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
+
+            final int previewPosX =
+                    (int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
+            final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY) / folderScale);
+
+            final float xDistance = previewPosX - btvLp.x;
+            final float yDistance = previewPosY - btvLp.y;
+
+            Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
+            translationX.setInterpolator(previewItemInterpolator);
+            play(animatorSet, translationX);
+
+            Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
+            translationY.setInterpolator(previewItemInterpolator);
+            play(animatorSet, translationY);
+
+            Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
+            scaleAnimator.setInterpolator(previewItemInterpolator);
+            play(animatorSet, scaleAnimator);
+
+            if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+                // These delays allows the preview items to move as part of the Folder's motion,
+                // and its only necessary for large folders because of differing interpolators.
+                int delay = mIsOpening ? mDelay : mDelay * 2;
+                if (mIsOpening) {
+                    translationX.setStartDelay(delay);
+                    translationY.setStartDelay(delay);
+                    scaleAnimator.setStartDelay(delay);
+                }
+                translationX.setDuration(translationX.getDuration() - delay);
+                translationY.setDuration(translationY.getDuration() - delay);
+                scaleAnimator.setDuration(scaleAnimator.getDuration() - delay);
+            }
+
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    super.onAnimationStart(animation);
+                    // Necessary to initialize values here because of the start delay.
+                    if (mIsOpening) {
+                        btv.setTranslationX(xDistance);
+                        btv.setTranslationY(yDistance);
+                        btv.setScaleX(initialScale);
+                        btv.setScaleY(initialScale);
+                    }
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    btv.setTranslationX(0.0f);
+                    btv.setTranslationY(0.0f);
+                    btv.setScaleX(1f);
+                    btv.setScaleY(1f);
+                }
+            });
+        }
+    }
+
+    private void play(AnimatorSet as, Animator a) {
+        play(as, a, a.getStartDelay(), mDuration);
+    }
+
+    private void play(AnimatorSet as, Animator a, long startDelay, int duration) {
+        a.setStartDelay(startDelay);
+        a.setDuration(duration);
+        as.play(a);
+    }
+
+    private TimeInterpolator getPreviewItemInterpolator() {
+        if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+            // With larger folders, we want the preview items to reach their final positions faster
+            // (when opening) and later (when closing) so that they appear aligned with the rest of
+            // the folder items when they are both visible.
+            return mIsOpening
+                    ? mLargeFolderPreviewItemOpenInterpolator
+                    : mLargeFolderPreviewItemCloseInterpolator;
+        }
+        return mFolderInterpolator;
+    }
+
+    private Animator getAnimator(View view, Property property, float v1, float v2) {
+        return mIsOpening
+                ? ObjectAnimator.ofFloat(view, property, v1, v2)
+                : ObjectAnimator.ofFloat(view, property, v2, v1);
+    }
+
+    private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
+        return mIsOpening
+                ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
+                : ObjectAnimator.ofArgb(drawable, property, v2, v1);
+    }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 8d58fef..8339bc5 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -19,25 +19,15 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.Point;
-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.graphics.drawable.Drawable;
 import android.os.Parcelable;
+import android.support.annotation.NonNull;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
 import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -47,7 +37,6 @@
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.TextView;
 
 import com.android.launcher3.Alarm;
 import com.android.launcher3.AppInfo;
@@ -56,7 +45,6 @@
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.FolderInfo.FolderListener;
 import com.android.launcher3.ItemInfo;
@@ -73,14 +61,18 @@
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.badge.FolderBadgeInfo;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.BaseItemDragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
+
 /**
  * An icon that can appear on in the workspace representing an {@link Folder}.
  */
@@ -97,11 +89,7 @@
     private CheckLongPressHelper mLongPressHelper;
     private StylusEventHelper mStylusEventHelper;
 
-    // The number of icons to display in the
-    private static final int CONSUMPTION_ANIMATION_DURATION = 100;
-    private static final int DROP_IN_ANIMATION_DURATION = 400;
-    private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
-    private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
+    static final int DROP_IN_ANIMATION_DURATION = 400;
 
     // Flag whether the folder should open itself when an item is dragged over is enabled.
     public static final boolean SPRING_LOADING_ENABLED = true;
@@ -111,25 +99,19 @@
 
     @Thunk BubbleTextView mFolderName;
 
-    // These variables are all associated with the drawing of the preview; they are stored
-    // as member variables for shared usage and to avoid computation on each frame
-    private int mIntrinsicIconSize = -1;
-    private int mTotalWidth = -1;
-    private int mPrevTopPadding = -1;
-
     PreviewBackground mBackground = new PreviewBackground();
+    private boolean mBackgroundIsVisible = true;
 
-    private PreviewLayoutRule mPreviewLayoutRule;
+    FolderIconPreviewVerifier mPreviewVerifier;
+    PreviewLayoutRule mPreviewLayoutRule;
+    private PreviewItemManager mPreviewItemManager;
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
 
     boolean mAnimating = false;
     private Rect mTempBounds = new Rect();
 
     private float mSlop;
 
-    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
-    private Drawable mReferenceDrawable = null;
-
     private Alarm mOpenAlarm = new Alarm();
 
     private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
@@ -168,6 +150,7 @@
                 new StackFolderIconLayoutRule() :
                 new ClippedFolderIconLayoutRule();
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        mPreviewItemManager = new PreviewItemManager(this);
     }
 
     public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -181,7 +164,8 @@
         }
 
         DeviceProfile grid = launcher.getDeviceProfile();
-        FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
+        FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
+                .inflate(resId, group, false);
 
         icon.setClipToPadding(false);
         icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
@@ -221,7 +205,8 @@
 
     private void setFolder(Folder folder) {
         mFolder = folder;
-        updateItemDrawingParams(false);
+        mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+        mPreviewItemManager.updateItemDrawingParams(false);
     }
 
     private boolean willAcceptItem(ItemInfo item) {
@@ -233,12 +218,19 @@
     }
 
     public boolean acceptDrop(ItemInfo dragInfo) {
-        final ItemInfo item = dragInfo;
-        return !mFolder.isDestroyed() && willAcceptItem(item);
+        return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
     public void addItem(ShortcutInfo item) {
-        mInfo.add(item, true);
+        addItem(item, true);
+    }
+
+    public void addItem(ShortcutInfo item, boolean animate) {
+        mInfo.add(item, animate);
+    }
+
+    public void removeItem(ShortcutInfo item, boolean animate) {
+        mInfo.remove(item, animate);
     }
 
     public void onDragEnter(ItemInfo dragInfo) {
@@ -249,11 +241,9 @@
         mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
         if (SPRING_LOADING_ENABLED &&
-                ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
-            // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
-            // though widget-style shortcuts can be added to folders. The issue is that we need
-            // to deal with configuration activities which are currently handled in
-            // Workspace#onDropExternal.
+                ((dragInfo instanceof AppInfo)
+                        || (dragInfo instanceof ShortcutInfo)
+                        || (dragInfo instanceof PendingAddShortcutInfo))) {
             mOpenAlarm.setAlarm(ON_OPEN_DELAY);
         }
     }
@@ -265,40 +255,28 @@
         }
     };
 
-    public Drawable prepareCreate(final View destView) {
-        Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
-                destView.getMeasuredWidth());
-        return animateDrawable;
+    public Drawable prepareCreateAnimation(final View destView) {
+        return mPreviewItemManager.prepareCreateAnimation(destView);
     }
 
     public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
             final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
-
-        // These correspond two the drawable and view that the icon was dropped _onto_
-        Drawable animateDrawable = prepareCreate(destView);
-
-        mReferenceDrawable = animateDrawable;
-
+        prepareCreateAnimation(destView);
         addItem(destInfo);
         // This will animate the first item from it's position as an icon into its
         // position as the first item in the preview
-        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
+        mPreviewItemManager.createFirstItemAnimation(false /* reverse */, null)
+                .start();
 
         // This will animate the dragView (srcView) into the new folder
         onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
     }
 
-    public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
-        Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
-                finalView.getMeasuredWidth());
-
-        // This will animate the first item from it's position as an icon into its
-        // position as the first item in the preview
-        animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
-                onCompleteRunnable);
+    public void performDestroyAnimation(Runnable onCompleteRunnable) {
+        // This will animate the final item in the preview to be full size.
+        mPreviewItemManager.createFirstItemAnimation(true /* reverse */, onCompleteRunnable)
+                .start();
     }
 
     public void onDragExit() {
@@ -335,13 +313,39 @@
                 workspace.resetTransitionTransform((CellLayout) getParent().getParent());
             }
 
+            boolean itemAdded = false;
+            if (index >= mPreviewLayoutRule.maxNumItems()
+                    && mPreviewLayoutRule.hasEnterExitIndices()) {
+                List<BubbleTextView> oldPreviewItems = getPreviewItemsOnPage(0);
+                addItem(item, false);
+                List<BubbleTextView> newPreviewItems = getPreviewItemsOnPage(0);
+
+                if (!oldPreviewItems.containsAll(newPreviewItems)) {
+                    for (int i = 0; i < newPreviewItems.size(); ++i) {
+                        if (newPreviewItems.get(i).getTag().equals(item)) {
+                            // If the item dropped is going to be in the preview, we update the
+                            // index here to reflect its position in the preview.
+                            index = i;
+                        }
+                    }
+                    mPreviewItemManager.onDrop(oldPreviewItems, newPreviewItems, item);
+                    itemAdded = true;
+                } else {
+                    removeItem(item, false);
+                }
+            }
+
+            if (!itemAdded) {
+                addItem(item);
+            }
+
             int[] center = new int[2];
             float scale = getLocalCenterForIndex(index, index + 1, center);
             center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
             center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
 
             to.offset(center[0] - animateView.getMeasuredWidth() / 2,
-                      center[1] - animateView.getMeasuredHeight() / 2);
+                    center[1] - animateView.getMeasuredHeight() / 2);
 
             float finalAlpha = index < mPreviewLayoutRule.maxNumItems() ? 0.5f : 0f;
 
@@ -350,15 +354,14 @@
                     1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                     new DecelerateInterpolator(2), new AccelerateInterpolator(2),
                     postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
-            addItem(item);
+
             mFolder.hideItem(item);
 
-            final PreviewItemDrawingParams params = index < mDrawingParams.size() ?
-                    mDrawingParams.get(index) : null;
-            if (params != null) params.hidden = true;
+            if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
+            final int finalIndex = index;
             postDelayed(new Runnable() {
                 public void run() {
-                    if (params != null) params.hidden = false;
+                    mPreviewItemManager.hidePreviewItem(finalIndex, false);
                     mFolder.showItem(item);
                     invalidate();
                 }
@@ -373,6 +376,9 @@
         if (d.dragInfo instanceof AppInfo) {
             // Came from all apps -- make a copy
             item = ((AppInfo) d.dragInfo).makeShortcut();
+        } else if (d.dragSource instanceof BaseItemDragListener){
+            // Came from a different window -- make a copy
+            item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
         } else {
             item = (ShortcutInfo) d.dragInfo;
         }
@@ -380,33 +386,15 @@
         onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
     }
 
-    private void computePreviewDrawingParams(int drawableSize, int totalSize) {
-        if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize ||
-                mPrevTopPadding != getPaddingTop()) {
-            DeviceProfile grid = mLauncher.getDeviceProfile();
-
-            mIntrinsicIconSize = drawableSize;
-            mTotalWidth = totalSize;
-            mPrevTopPadding = getPaddingTop();
-
-            mBackground.setup(getResources().getDisplayMetrics(), grid, this, mTotalWidth,
-                    getPaddingTop());
-            mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
-                    Utilities.isRtl(getResources()));
-
-            updateItemDrawingParams(false);
-        }
-    }
-
-    private void computePreviewDrawingParams(Drawable d) {
-        computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
-    }
-
     public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
         updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
         mBadgeInfo = badgeInfo;
     }
 
+    public PreviewLayoutRule getLayoutRule() {
+        return mPreviewLayoutRule;
+    }
+
     /**
      * Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
      * (the badge is being added or removed).
@@ -415,427 +403,62 @@
         float newBadgeScale = isBadged ? 1f : 0f;
         // Animate when a badge is first added or when it is removed.
         if ((wasBadged ^ isBadged) && isShown()) {
-            ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
+            createBadgeScaleAnimator(newBadgeScale).start();
         } else {
             mBadgeScale = newBadgeScale;
             invalidate();
         }
     }
 
-    static class PreviewItemDrawingParams {
-        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-            this.overlayAlpha = overlayAlpha;
-        }
+    public Animator createBadgeScaleAnimator(float... badgeScales) {
+        return ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, badgeScales);
+    }
 
-        public void update(float transX, float transY, float scale) {
-            // 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) {
-                    return;
-                }
-                anim.cancel();
-            }
-
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-        }
-
-        float transX;
-        float transY;
-        float scale;
-        public float overlayAlpha;
-        boolean hidden;
-        FolderPreviewItemAnim anim;
-        Drawable drawable;
+    public boolean hasBadge() {
+        return mBadgeInfo != null && mBadgeInfo.hasBadge();
     }
 
     private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
-        mTmpParams = computePreviewItemDrawingParams(
+        mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams(
                 Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams);
 
         mTmpParams.transX += mBackground.basePreviewOffsetX;
         mTmpParams.transY += mBackground.basePreviewOffsetY;
-        float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
-        float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
 
-        center[0] = (int) Math.round(offsetX);
-        center[1] = (int) Math.round(offsetY);
+        float intrinsicIconSize = mPreviewItemManager.getIntrinsicIconSize();
+        float offsetX = mTmpParams.transX + (mTmpParams.scale * intrinsicIconSize) / 2;
+        float offsetY = mTmpParams.transY + (mTmpParams.scale * intrinsicIconSize) / 2;
+
+        center[0] = Math.round(offsetX);
+        center[1] = Math.round(offsetY);
         return mTmpParams.scale;
     }
 
-    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
-            PreviewItemDrawingParams params) {
-        // We use an index of -1 to represent an icon on the workspace for the destroy and
-        // create animations
-        if (index == -1) {
-            return getFinalIconParams(params);
-        }
-        return mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params);
-    }
-
-    private PreviewItemDrawingParams getFinalIconParams(PreviewItemDrawingParams params) {
-        float iconSize = mLauncher.getDeviceProfile().iconSizePx;
-
-        final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
-        final float trans = (mBackground.previewSize - iconSize) / 2;
-
-        params.update(trans, trans, scale);
-        return params;
-    }
-
-    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
-        canvas.save(Canvas.MATRIX_SAVE_FLAG);
-        canvas.translate(params.transX, params.transY);
-        canvas.scale(params.scale, params.scale);
-        Drawable d = params.drawable;
-
-        if (d != null) {
-            mTempBounds.set(d.getBounds());
-            d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
-            if (d instanceof FastBitmapDrawable) {
-                FastBitmapDrawable fd = (FastBitmapDrawable) d;
-                fd.drawWithBrightness(canvas, params.overlayAlpha);
-            } else {
-                d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
-                        PorterDuff.Mode.SRC_ATOP);
-                d.draw(canvas);
-                d.clearColorFilter();
-            }
-            d.setBounds(mTempBounds);
-        }
-        canvas.restore();
-    }
-
-    /**
-     * This object represents a FolderIcon preview background. It stores drawing / measurement
-     * information, handles drawing, and animation (accept state <--> rest state).
-     */
-    public static class PreviewBackground {
-
-        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;
-
-        private final Matrix mShaderMatrix = new Matrix();
-        private final Path mPath = new Path();
-
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-        private float mScale = 1f;
-        private float mColorMultiplier = 1f;
-        private float mStrokeWidth;
-        private View mInvalidateDelegate;
-
-        public int previewSize;
-        private int basePreviewOffsetX;
-        private int basePreviewOffsetY;
-
-        private CellLayout mDrawingDelegate;
-        public int delegateCellX;
-        public int delegateCellY;
-
-        // When the PreviewBackground is drawn under an icon (for creating a folder) the border
-        // should not occlude the icon
-        public boolean isClipping = true;
-
-        // Drawing / animation configurations
-        private static final float ACCEPT_SCALE_FACTOR = 1.25f;
-        private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
-
-        // Expressed on a scale from 0 to 255.
-        private static final int BG_OPACITY = 160;
-        private static final int MAX_BG_OPACITY = 225;
-        private static final int BG_INTENSITY = 245;
-        private static final int SHADOW_OPACITY = 40;
-
-        ValueAnimator mScaleAnimator;
-
-        public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDelegate,
-                   int availableSpace, int topPadding) {
-            mInvalidateDelegate = invalidateDelegate;
-
-            final int previewSize = grid.folderIconSizePx;
-            final int previewPadding = grid.folderIconPreviewPadding;
-
-            this.previewSize = (previewSize - 2 * previewPadding);
-
-            basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
-            basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
-
-            // Stroke width is 1dp
-            mStrokeWidth = dm.density;
-
-            float radius = getScaledRadius();
-            float shadowRadius = radius + mStrokeWidth;
-            int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
-            mShadowShader = new RadialGradient(0, 0, 1,
-                    new int[] {shadowColor, Color.TRANSPARENT},
-                    new float[] {radius / shadowRadius, 1},
-                    Shader.TileMode.CLAMP);
-
-            invalidate();
-        }
-
-        int getRadius() {
-            return previewSize / 2;
-        }
-
-        int getScaledRadius() {
-            return (int) (mScale * getRadius());
-        }
-
-        int getOffsetX() {
-            return basePreviewOffsetX - (getScaledRadius() - getRadius());
-        }
-
-        int getOffsetY() {
-            return basePreviewOffsetY - (getScaledRadius() - getRadius());
-        }
-
-        /**
-         * Returns the progress of the scale animation, where 0 means the scale is at 1f
-         * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
-         */
-        float getScaleProgress() {
-            return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
-        }
-
-        void invalidate() {
-            if (mInvalidateDelegate != null) {
-                mInvalidateDelegate.invalidate();
-            }
-
-            if (mDrawingDelegate != null) {
-                mDrawingDelegate.invalidate();
-            }
-        }
-
-        void setInvalidateDelegate(View invalidateDelegate) {
-            mInvalidateDelegate = invalidateDelegate;
-            invalidate();
-        }
-
-        public void drawBackground(Canvas canvas) {
-            mPaint.setStyle(Paint.Style.FILL);
-            int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-            mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
-
-            drawCircle(canvas, 0 /* deltaRadius */);
-
-            // Draw shadow.
-            if (mShadowShader == null) {
-                return;
-            }
-            float radius = getScaledRadius();
-            float shadowRadius = radius + mStrokeWidth;
-            mPaint.setColor(Color.BLACK);
-            int offsetX = getOffsetX();
-            int offsetY = getOffsetY();
-            final int saveCount;
-            if (canvas.isHardwareAccelerated()) {
-                saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
-                        offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
-                        null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
-
-            } else {
-                saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-                clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
-            }
-
-            mShaderMatrix.setScale(shadowRadius, shadowRadius);
-            mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
-            mShadowShader.setLocalMatrix(mShaderMatrix);
-            mPaint.setShader(mShadowShader);
-            canvas.drawPaint(mPaint);
-            mPaint.setShader(null);
-
-            if (canvas.isHardwareAccelerated()) {
-                mPaint.setXfermode(mShadowPorterDuffXfermode);
-                canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
-                mPaint.setXfermode(null);
-            }
-
-            canvas.restoreToCount(saveCount);
-        }
-
-        public void drawBackgroundStroke(Canvas canvas) {
-            mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
-            mPaint.setStyle(Paint.Style.STROKE);
-            mPaint.setStrokeWidth(mStrokeWidth);
-            drawCircle(canvas, 1 /* deltaRadius */);
-        }
-
-        public void drawLeaveBehind(Canvas canvas) {
-            float originalScale = mScale;
-            mScale = 0.5f;
-
-            mPaint.setStyle(Paint.Style.FILL);
-            mPaint.setColor(Color.argb(160, 245, 245, 245));
-            drawCircle(canvas, 0 /* deltaRadius */);
-
-            mScale = originalScale;
-        }
-
-        private void drawCircle(Canvas canvas,float deltaRadius) {
-            float radius = getScaledRadius();
-            canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
-                    radius - deltaRadius, mPaint);
-        }
-
-        // It is the callers responsibility to save and restore the canvas layers.
-        private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
-            mPath.reset();
-            float r = getScaledRadius();
-            mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
-            canvas.clipPath(mPath, op);
-        }
-
-        // It is the callers responsibility to save and restore the canvas layers.
-        private void clipCanvasHardware(Canvas canvas) {
-            mPaint.setColor(Color.BLACK);
-            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);
-            }
-
-            mDrawingDelegate = delegate;
-            delegateCellX = cellX;
-            delegateCellY = cellY;
-
-            invalidate();
-        }
-
-        private void clearDrawingDelegate() {
-            if (mDrawingDelegate != null) {
-                mDrawingDelegate.removeFolderBackground(this);
-            }
-
-            mDrawingDelegate = null;
-            invalidate();
-        }
-
-        private boolean drawingDelegated() {
-            return mDrawingDelegate != null;
-        }
-
-        private void animateScale(float finalScale, float finalMultiplier,
-                final Runnable onStart, final Runnable onEnd) {
-            final float scale0 = mScale;
-            final float scale1 = finalScale;
-
-            final float bgMultiplier0 = mColorMultiplier;
-            final float bgMultiplier1 = finalMultiplier;
-
-            if (mScaleAnimator != null) {
-                mScaleAnimator.cancel();
-            }
-
-            mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-
-            mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float prog = animation.getAnimatedFraction();
-                    mScale = prog * scale1 + (1 - prog) * scale0;
-                    mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
-                    invalidate();
-                }
-            });
-            mScaleAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    if (onStart != null) {
-                        onStart.run();
-                    }
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (onEnd != null) {
-                        onEnd.run();
-                    }
-                    mScaleAnimator = null;
-                }
-            });
-
-            mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
-            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 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);
-        }
-    }
-
     public void setFolderBackground(PreviewBackground bg) {
         mBackground = bg;
         mBackground.setInvalidateDelegate(this);
     }
 
+    public void setBackgroundVisible(boolean visible) {
+        mBackgroundIsVisible = visible;
+        invalidate();
+    }
+
+    public PreviewBackground getFolderBackground() {
+        return mBackground;
+    }
+
+    public PreviewItemManager getPreviewItemManager() {
+        return mPreviewItemManager;
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
-        if (mReferenceDrawable != null) {
-            computePreviewDrawingParams(mReferenceDrawable);
-        }
+        if (!mBackgroundIsVisible) return;
+
+        mPreviewItemManager.recomputePreviewDrawingParams();
 
         if (!mBackground.drawingDelegated()) {
             mBackground.drawBackground(canvas);
@@ -852,21 +475,11 @@
         } else {
             saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
             if (mPreviewLayoutRule.clipToBackground()) {
-                mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
+                canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
             }
         }
 
-        // The items are drawn in coordinates relative to the preview offset
-        canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
-
-        // The first item should be drawn last (ie. on top of later items)
-        for (int i = mDrawingParams.size() - 1; i >= 0; i--) {
-            PreviewItemDrawingParams p = mDrawingParams.get(i);
-            if (!p.hidden) {
-                drawPreviewItem(canvas, p);
-            }
-        }
-        canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
+        mPreviewItemManager.draw(canvas);
 
         if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
             mBackground.clipCanvasHardware(canvas);
@@ -877,6 +490,10 @@
             mBackground.drawBackgroundStroke(canvas);
         }
 
+        drawBadge(canvas);
+    }
+
+    public void drawBadge(Canvas canvas) {
         if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
             int offsetX = mBackground.getOffsetX();
             int offsetY = mBackground.getOffsetY();
@@ -892,94 +509,6 @@
         }
     }
 
-    class FolderPreviewItemAnim {
-        ValueAnimator mValueAnimator;
-        float finalScale;
-        float finalTransX;
-        float finalTransY;
-
-        /**
-         *
-         * @param params layout params to animate
-         * @param index0 original index of the item to be animated
-         * @param nItems0 original number of items in the preview
-         * @param index1 new index of the item to be animated
-         * @param nItems1 new number of items in the preview
-         * @param duration duration in ms of the animation
-         * @param onCompleteRunnable runnable to execute upon animation completion
-         */
-        public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
-                int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
-
-            computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
-
-            finalScale = mTmpParams.scale;
-            finalTransX = mTmpParams.transX;
-            finalTransY = mTmpParams.transY;
-
-            computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
-
-            final float scale0 = mTmpParams.scale;
-            final float transX0 = mTmpParams.transX;
-            final float transY0 = mTmpParams.transY;
-
-            mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-            mValueAnimator.addUpdateListener(new 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);
-                    invalidate();
-                }
-            });
-
-            mValueAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (onCompleteRunnable != null) {
-                        onCompleteRunnable.run();
-                    }
-                    params.anim = null;
-                }
-            });
-            mValueAnimator.setDuration(duration);
-        }
-
-        public void start() {
-            mValueAnimator.start();
-        }
-
-        public void cancel() {
-            mValueAnimator.cancel();
-        }
-
-        public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
-            return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
-                    finalScale == anim.finalScale;
-
-        }
-    }
-
-    private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
-            final Runnable onCompleteRunnable) {
-
-        FolderPreviewItemAnim anim;
-        if (!reverse) {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
-                    onCompleteRunnable);
-        } else {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
-                    onCompleteRunnable);
-        }
-        anim.start();
-    }
-
     public void setTextVisible(boolean visible) {
         if (visible) {
             mFolderName.setVisibility(VISIBLE);
@@ -992,49 +521,42 @@
         return mFolderName.getVisibility() == VISIBLE;
     }
 
-    private void updateItemDrawingParams(boolean animate) {
-        List<View> items = mPreviewLayoutRule.getItemsToDisplay(mFolder);
-        int nItemsInPreview = items.size();
+    /**
+     * Returns the list of preview items displayed in the icon.
+     */
+    public List<BubbleTextView> getPreviewItems() {
+        return getPreviewItemsOnPage(0);
+    }
 
-        int prevNumItems = mDrawingParams.size();
+    /**
+     * Returns the list of "preview items" on {@param page}.
+     */
+    public List<BubbleTextView> getPreviewItemsOnPage(int page) {
+        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
 
-        // We adjust the size of the list to match the number of items in the preview
-        while (nItemsInPreview < mDrawingParams.size()) {
-            mDrawingParams.remove(mDrawingParams.size() - 1);
-        }
-        while (nItemsInPreview > mDrawingParams.size()) {
-            mDrawingParams.add(new PreviewItemDrawingParams(0, 0, 0, 0));
-        }
+        List<BubbleTextView> itemsToDisplay = new ArrayList<>();
+        List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
+        int numItems = itemsOnPage.size();
+        for (int rank = 0; rank < numItems; ++rank) {
+            if (mPreviewVerifier.isItemInPreview(page, rank)) {
+                itemsToDisplay.add(itemsOnPage.get(rank));
+            }
 
-        for (int i = 0; i < mDrawingParams.size(); i++) {
-            PreviewItemDrawingParams p = mDrawingParams.get(i);
-            p.drawable = ((TextView) items.get(i)).getCompoundDrawables()[1];
-
-            if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
-                computePreviewItemDrawingParams(i, nItemsInPreview, p);
-                if (mReferenceDrawable == null) {
-                    mReferenceDrawable = p.drawable;
-                }
-            } else {
-                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
-                        nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
-
-                if (p.anim != null) {
-                    if (p.anim.hasEqualFinalState(anim)) {
-                        // do nothing, let the current animation finish
-                        continue;
-                    }
-                    p.anim.cancel();
-                }
-                p.anim = anim;
-                p.anim.start();
+            if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+                break;
             }
         }
+        return itemsToDisplay;
+    }
+
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable who) {
+        return mPreviewItemManager.verifyDrawable(who) || super.verifyDrawable(who);
     }
 
     @Override
     public void onItemsChanged(boolean animate) {
-        updateItemDrawingParams(animate);
+        mPreviewItemManager.updateItemDrawingParams(animate);
         invalidate();
         requestLayout();
     }
@@ -1044,7 +566,7 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item) {
+    public void onAdd(ShortcutInfo item, int rank) {
         boolean wasBadged = mBadgeInfo.hasBadge();
         mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
         boolean isBadged = mBadgeInfo.hasBadge();
@@ -1110,28 +632,21 @@
     }
 
     public void shrinkAndFadeIn(boolean animate) {
-        final CellLayout cl = (CellLayout) getParent().getParent();
-        ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
-
         // We remove and re-draw the FolderIcon in-case it has changed
         final PreviewImageView previewImage = PreviewImageView.get(getContext());
         previewImage.removeFromParent();
         copyToPreview(previewImage);
 
-        if (cl != null) {
-            cl.clearFolderLeaveBehind();
-        }
+        clearLeaveBehindIfExists();
 
         ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1);
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (cl != null) {
-                    // Remove the ImageView copy of the FolderIcon and make the original visible.
-                    previewImage.removeFromParent();
-                    setVisibility(View.VISIBLE);
-                }
+                // Remove the ImageView copy of the FolderIcon and make the original visible.
+                previewImage.removeFromParent();
+                setVisibility(View.VISIBLE);
             }
         });
         oa.start();
@@ -1140,7 +655,15 @@
         }
     }
 
-    public void growAndFadeOut() {
+    public void clearLeaveBehindIfExists() {
+        ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+        if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            CellLayout cl = (CellLayout) getParent().getParent();
+            cl.clearFolderLeaveBehind();
+        }
+    }
+
+    public void drawLeaveBehindIfExists() {
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
         // While the folder is open, the position of the icon cannot change.
         lp.canReorder = false;
@@ -1148,6 +671,10 @@
             CellLayout cl = (CellLayout) getParent().getParent();
             cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
         }
+    }
+
+    public void growAndFadeOut() {
+        drawLeaveBehindIfExists();
 
         // Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
         PreviewImageView previewImage = PreviewImageView.get(getContext());
@@ -1172,13 +699,22 @@
         }
     }
 
-    public interface PreviewLayoutRule {
+    public void onFolderClose(int currentPage) {
+        mPreviewItemManager.onFolderClose(currentPage);
+    }
+
+    interface PreviewLayoutRule {
         PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
-            PreviewItemDrawingParams params);
-        void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+                PreviewItemDrawingParams params);
+        void init(int availableSpace, float intrinsicIconSize, boolean rtl);
         float scaleForItem(int index, int totalNumItems);
+        float getIconSize();
         int maxNumItems();
         boolean clipToBackground();
-        List<View> getItemsToDisplay(Folder folder);
+
+        boolean hasEnterExitIndices();
+        int getExitIndex();
+        int getEnterIndex();
+
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
new file mode 100644
index 0000000..d054a5d
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -0,0 +1,75 @@
+/*
+ * 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.folder;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
+ */
+public class FolderIconPreviewVerifier {
+
+    private final int mMaxGridCountX;
+    private final int mMaxGridCountY;
+    private final int mMaxItemsPerPage;
+    private final int[] mGridSize = new int[2];
+
+    private int mGridCountX;
+    private boolean mDisplayingUpperLeftQuadrant = false;
+
+    public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
+        mMaxGridCountX = profile.numFolderColumns;
+        mMaxGridCountY = profile.numFolderRows;
+        mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
+    }
+
+    public void setFolderInfo(FolderInfo info) {
+        int numItemsInFolder = info.contents.size();
+        FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
+                mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+        mGridCountX = mGridSize[0];
+
+        mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+                && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
+                && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
+    }
+
+    /**
+     * Returns whether the item with {@param rank} is in the default Folder icon preview.
+     */
+    public boolean isItemInPreview(int rank) {
+        return isItemInPreview(0, rank);
+    }
+
+    /**
+     * @param page The page the item is on.
+     * @param rank The rank of the item.
+     * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
+     */
+    public boolean isItemInPreview(int page, int rank) {
+        // 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) {
+            int col = rank % mGridCountX;
+            int row = rank / mGridCountX;
+            return col < 2 && row < 2;
+        }
+        return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
+    }
+}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index eecce18..f4ac0a1 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,8 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -35,21 +37,17 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
@@ -68,14 +66,14 @@
      */
     private static final float SCROLL_HINT_FRACTION = 0.07f;
 
-    private static final int[] sTempPosArray = new int[2];
+    private static final int[] sTmpArray = new int[2];
 
     public final boolean mIsRtl;
 
     private final LayoutInflater mInflater;
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
 
-    @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
+    @Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private final int mMaxCountX;
@@ -108,52 +106,69 @@
         mIsRtl = Utilities.isRtl(getResources());
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
 
-        setEdgeGlowColor(Themes.getAttrColor(context, android.R.attr.colorEdgeEffect));
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
     }
 
     public void setFolder(Folder folder) {
         mFolder = folder;
         mKeyListener = new PagedFolderKeyEventListener(folder);
-        mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
+        mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
         initParentViews(folder);
     }
 
     /**
-     * Sets up the grid size such that {@param count} items can fit in the grid.
+     * Calculates the grid size such that {@param count} items can fit in the grid.
      * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
      * maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
      */
-    private void setupContentDimensions(int count) {
-        mAllocatedContentSize = count;
+    public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
+            int maxCountY, int maxItemsPerPage, int[] out) {
         boolean done;
-        if (count >= mMaxItemsPerPage) {
-            mGridCountX = mMaxCountX;
-            mGridCountY = mMaxCountY;
+        int gridCountX = countX;
+        int gridCountY = countY;
+
+        if (count >= maxItemsPerPage) {
+            gridCountX = maxCountX;
+            gridCountY = maxCountY;
             done = true;
         } else {
             done = false;
         }
 
         while (!done) {
-            int oldCountX = mGridCountX;
-            int oldCountY = mGridCountY;
-            if (mGridCountX * mGridCountY < count) {
+            int oldCountX = gridCountX;
+            int oldCountY = gridCountY;
+            if (gridCountX * gridCountY < count) {
                 // Current grid is too small, expand it
-                if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) {
-                    mGridCountX++;
-                } else if (mGridCountY < mMaxCountY) {
-                    mGridCountY++;
+                if ((gridCountX <= gridCountY || gridCountY == maxCountY)
+                        && gridCountX < maxCountX) {
+                    gridCountX++;
+                } else if (gridCountY < maxCountY) {
+                    gridCountY++;
                 }
-                if (mGridCountY == 0) mGridCountY++;
-            } else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) {
-                mGridCountY = Math.max(0, mGridCountY - 1);
-            } else if ((mGridCountX - 1) * mGridCountY >= count) {
-                mGridCountX = Math.max(0, mGridCountX - 1);
+                if (gridCountY == 0) gridCountY++;
+            } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+                gridCountY = Math.max(0, gridCountY - 1);
+            } else if ((gridCountX - 1) * gridCountY >= count) {
+                gridCountX = Math.max(0, gridCountX - 1);
             }
-            done = mGridCountX == oldCountX && mGridCountY == oldCountY;
+            done = gridCountX == oldCountX && gridCountY == oldCountY;
         }
 
+        out[0] = gridCountX;
+        out[1] = gridCountY;
+    }
+
+    /**
+     * Sets up the grid size such that {@param count} items can fit in the grid.
+     */
+    public void setupContentDimensions(int count) {
+        mAllocatedContentSize = count;
+        calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
+                sTmpArray);
+        mGridCountX = sTmpArray[0];
+        mGridCountY = sTmpArray[1];
+
         // Update grid size
         for (int i = getPageCount() - 1; i >= 0; i--) {
             getPageAt(i).setGridSize(mGridCountX, mGridCountY);
@@ -171,8 +186,8 @@
      * @return list of items that could not be bound, probably because we hit the max size limit.
      */
     public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
-        ArrayList<View> icons = new ArrayList<View>();
-        ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
+        ArrayList<View> icons = new ArrayList<>();
+        ArrayList<ShortcutInfo> extra = new ArrayList<>();
 
         for (ShortcutInfo item : items) {
             if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
@@ -185,21 +200,26 @@
         return extra;
     }
 
+    public void allocateSpaceForRank(int rank) {
+        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+        views.add(rank, null);
+        arrangeChildren(views, views.size(), false);
+    }
+
     /**
      * Create space for a new item at the end, and returns the rank for that item.
      * Also sets the current page to the last page.
      */
     public int allocateRankForNewItem() {
         int rank = getItemCount();
-        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
-        views.add(rank, null);
-        arrangeChildren(views, views.size(), false);
+        allocateSpaceForRank(rank);
         setCurrentPage(rank / mMaxItemsPerPage);
         return rank;
     }
 
     public View createAndAddViewForRank(ShortcutInfo item, int rank) {
         View icon = createNewView(item);
+        allocateSpaceForRank(rank);
         addViewForRank(icon, item, rank);
         return icon;
     }
@@ -228,6 +248,7 @@
         final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
                 R.layout.folder_application, null, false);
         textView.applyFromShortcutInfo(item);
+        textView.setHapticFeedbackEnabled(false);
         textView.setOnClickListener(mFolder);
         textView.setOnLongClickListener(mFolder);
         textView.setOnFocusChangeListener(mFocusIndicatorHelper);
@@ -300,7 +321,7 @@
 
     @SuppressLint("RtlHardcoded")
     private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
-        ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
+        ArrayList<CellLayout> pages = new ArrayList<>();
         for (int i = 0; i < getChildCount(); i++) {
             CellLayout page = (CellLayout) getChildAt(i);
             page.removeAllViews();
@@ -314,6 +335,8 @@
         int position = 0;
         int newX, newY, rank;
 
+        FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
+                Launcher.getLauncher(getContext()).getDeviceProfile().inv);
         rank = 0;
         for (int i = 0; i < itemCount; i++) {
             View v = list.size() > i ? list.get(i) : null;
@@ -346,7 +369,7 @@
                 currentPage.addViewToCellLayout(
                         v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
 
-                if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) {
+                if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
                     ((BubbleTextView) v).verifyHighRes();
                 }
             }
@@ -400,12 +423,12 @@
     public int findNearestArea(int pixelX, int pixelY) {
         int pageIndex = getNextPage();
         CellLayout page = getPageAt(pageIndex);
-        page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray);
+        page.findNearestArea(pixelX, pixelY, 1, 1, sTmpArray);
         if (mFolder.isLayoutRtl()) {
-            sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1;
+            sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
         }
         return Math.min(mAllocatedContentSize - 1,
-                pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
+                pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
     }
 
     public boolean isFull() {
@@ -471,8 +494,8 @@
     }
 
     @Override
-    protected void notifyPageSwitchListener() {
-        super.notifyPageSwitchListener();
+    protected void notifyPageSwitchListener(int prevPage) {
+        super.notifyPageSwitchListener(prevPage);
         if (mFolder != null) {
             mFolder.updateTextViewFocus();
         }
@@ -505,7 +528,7 @@
      */
     public void completePendingPageChanges() {
         if (!mPendingAnimations.isEmpty()) {
-            HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
+            ArrayMap<View, Runnable> pendingViews = new ArrayMap<>(mPendingAnimations);
             for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
                 e.getKey().animate().cancel();
                 e.getValue().run();
@@ -534,7 +557,14 @@
         if (page != null) {
             ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
             for (int i = parent.getChildCount() - 1; i >= 0; i--) {
-                ((BubbleTextView) parent.getChildAt(i)).verifyHighRes();
+                BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i));
+                icon.verifyHighRes();
+                // Set the callback back to the actual icon, in case
+                // it was captured by the FolderIcon
+                Drawable d = icon.getCompoundDrawables()[1];
+                if (d != null) {
+                    d.setCallback(icon);
+                }
             }
         }
     }
@@ -671,10 +701,4 @@
     public int itemsPerPage() {
         return mMaxItemsPerPage;
     }
-
-    @Override
-    protected void getEdgeVerticalPosition(int[] pos) {
-        pos[0] = 0;
-        pos[1] = getViewportHeight();
-    }
 }
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
new file mode 100644
index 0000000..be075bc
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.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.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.LauncherAnimUtils;
+
+/**
+ * Animates a Folder preview item.
+ */
+class FolderPreviewItemAnim {
+
+    private static PreviewItemDrawingParams sTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+    private ValueAnimator mValueAnimator;
+
+    float finalScale;
+    float finalTransX;
+    float finalTransY;
+
+    /**
+     * @param params layout params to animate
+     * @param index0 original index of the item to be animated
+     * @param items0 original number of items in the preview
+     * @param index1 new index of the item to be animated
+     * @param items1 new number of items in the preview
+     * @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,
+            int duration, final Runnable onCompleteRunnable) {
+        previewItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams);
+
+        finalScale = sTmpParams.scale;
+        finalTransX = sTmpParams.transX;
+        finalTransY = sTmpParams.transY;
+
+        previewItemManager.computePreviewItemDrawingParams(index0, items0, sTmpParams);
+
+        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() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
+                params.anim = null;
+            }
+        });
+        mValueAnimator.setDuration(duration);
+    }
+
+    public void start() {
+        mValueAnimator.start();
+    }
+
+    public void cancel() {
+        mValueAnimator.cancel();
+    }
+
+    public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
+        return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
+                finalScale == anim.finalScale;
+
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
new file mode 100644
index 0000000..eba5d98
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -0,0 +1,440 @@
+/*
+ * 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.folder;
+
+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.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RadialGradient;
+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;
+
+/**
+ * This object represents a FolderIcon preview background. It stores drawing / measurement
+ * information, handles drawing, and animation (accept state <--> rest state).
+ */
+public class PreviewBackground {
+
+    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;
+
+    private final Matrix mShaderMatrix = new Matrix();
+    private final Path mPath = new Path();
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+    float mScale = 1f;
+    private float mColorMultiplier = 1f;
+    private int mBgColor;
+    private float mStrokeWidth;
+    private int mStrokeAlpha = MAX_BG_OPACITY;
+    private int mShadowAlpha = 255;
+    private View mInvalidateDelegate;
+
+    int previewSize;
+    int basePreviewOffsetX;
+    int basePreviewOffsetY;
+
+    private CellLayout mDrawingDelegate;
+    public int delegateCellX;
+    public int delegateCellY;
+
+    // When the PreviewBackground is drawn under an icon (for creating a folder) the border
+    // should not occlude the icon
+    public boolean isClipping = true;
+
+    // Drawing / animation configurations
+    private static final float ACCEPT_SCALE_FACTOR = 1.20f;
+    private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
+
+    // Expressed on a scale from 0 to 255.
+    private static final int BG_OPACITY = 160;
+    private static final int MAX_BG_OPACITY = 225;
+    private static final int SHADOW_OPACITY = 40;
+
+    private ValueAnimator mScaleAnimator;
+    private ObjectAnimator mStrokeAlphaAnimator;
+    private ObjectAnimator mShadowAnimator;
+
+    private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
+            new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
+                @Override
+                public Integer get(PreviewBackground previewBackground) {
+                    return previewBackground.mStrokeAlpha;
+                }
+
+                @Override
+                public void set(PreviewBackground previewBackground, Integer alpha) {
+                    previewBackground.mStrokeAlpha = alpha;
+                    previewBackground.invalidate();
+                }
+            };
+
+    private static final Property<PreviewBackground, Integer> SHADOW_ALPHA =
+            new Property<PreviewBackground, Integer>(Integer.class, "shadowAlpha") {
+                @Override
+                public Integer get(PreviewBackground previewBackground) {
+                    return previewBackground.mShadowAlpha;
+                }
+
+                @Override
+                public void set(PreviewBackground previewBackground, Integer alpha) {
+                    previewBackground.mShadowAlpha = alpha;
+                    previewBackground.invalidate();
+                }
+            };
+
+    public void setup(Launcher launcher, View invalidateDelegate,
+                      int availableSpace, int topPadding) {
+        mInvalidateDelegate = invalidateDelegate;
+        mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
+
+        DeviceProfile grid = launcher.getDeviceProfile();
+        final int previewSize = grid.folderIconSizePx;
+        final int previewPadding = grid.folderIconPreviewPadding;
+
+        this.previewSize = (previewSize - 2 * previewPadding);
+
+        basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
+        basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+
+        // Stroke width is 1dp
+        mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
+
+        float radius = getScaledRadius();
+        float shadowRadius = radius + mStrokeWidth;
+        int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
+        mShadowShader = new RadialGradient(0, 0, 1,
+                new int[] {shadowColor, Color.TRANSPARENT},
+                new float[] {radius / shadowRadius, 1},
+                Shader.TileMode.CLAMP);
+
+        invalidate();
+    }
+
+    int getRadius() {
+        return previewSize / 2;
+    }
+
+    int getScaledRadius() {
+        return (int) (mScale * getRadius());
+    }
+
+    int getOffsetX() {
+        return basePreviewOffsetX - (getScaledRadius() - getRadius());
+    }
+
+    int getOffsetY() {
+        return basePreviewOffsetY - (getScaledRadius() - getRadius());
+    }
+
+    /**
+     * Returns the progress of the scale animation, where 0 means the scale is at 1f
+     * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
+     */
+    float getScaleProgress() {
+        return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
+    }
+
+    void invalidate() {
+        if (mInvalidateDelegate != null) {
+            mInvalidateDelegate.invalidate();
+        }
+
+        if (mDrawingDelegate != null) {
+            mDrawingDelegate.invalidate();
+        }
+    }
+
+    void setInvalidateDelegate(View invalidateDelegate) {
+        mInvalidateDelegate = invalidateDelegate;
+        invalidate();
+    }
+
+    public int getBgColor() {
+        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        return ColorUtils.setAlphaComponent(mBgColor, alpha);
+    }
+
+    public void drawBackground(Canvas canvas) {
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setColor(getBgColor());
+
+        drawCircle(canvas, 0 /* deltaRadius */);
+
+        drawShadow(canvas);
+    }
+
+    public void drawShadow(Canvas canvas) {
+        if (mShadowShader == null) {
+            return;
+        }
+
+        float radius = getScaledRadius();
+        float shadowRadius = radius + mStrokeWidth;
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setColor(Color.BLACK);
+        int offsetX = getOffsetX();
+        int offsetY = getOffsetY();
+        final int saveCount;
+        if (canvas.isHardwareAccelerated()) {
+            saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
+                    offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
+                    null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+
+        } else {
+            saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+            canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE);
+        }
+
+        mShaderMatrix.setScale(shadowRadius, shadowRadius);
+        mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
+        mShadowShader.setLocalMatrix(mShaderMatrix);
+        mPaint.setAlpha(mShadowAlpha);
+        mPaint.setShader(mShadowShader);
+        canvas.drawPaint(mPaint);
+        mPaint.setAlpha(255);
+        mPaint.setShader(null);
+        if (canvas.isHardwareAccelerated()) {
+            mPaint.setXfermode(mShadowPorterDuffXfermode);
+            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+            mPaint.setXfermode(null);
+        }
+
+        canvas.restoreToCount(saveCount);
+    }
+
+    public void fadeInBackgroundShadow() {
+        if (mShadowAnimator != null) {
+            mShadowAnimator.cancel();
+        }
+        mShadowAnimator = ObjectAnimator
+                .ofInt(this, SHADOW_ALPHA, 0, 255)
+                .setDuration(100);
+        mShadowAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mShadowAnimator = null;
+            }
+        });
+        mShadowAnimator.start();
+    }
+
+    public void animateBackgroundStroke() {
+        if (mStrokeAlphaAnimator != null) {
+            mStrokeAlphaAnimator.cancel();
+        }
+        mStrokeAlphaAnimator = ObjectAnimator
+                .ofInt(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
+                .setDuration(100);
+        mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStrokeAlphaAnimator = null;
+            }
+        });
+        mStrokeAlphaAnimator.start();
+    }
+
+    public void drawBackgroundStroke(Canvas canvas) {
+        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(mStrokeWidth);
+        drawCircle(canvas, 1 /* deltaRadius */);
+    }
+
+    public void drawLeaveBehind(Canvas canvas) {
+        float originalScale = mScale;
+        mScale = 0.5f;
+
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setColor(Color.argb(160, 245, 245, 245));
+        drawCircle(canvas, 0 /* deltaRadius */);
+
+        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);
+        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);
+        }
+
+        mDrawingDelegate = delegate;
+        delegateCellX = cellX;
+        delegateCellY = cellY;
+
+        invalidate();
+    }
+
+    private void clearDrawingDelegate() {
+        if (mDrawingDelegate != null) {
+            mDrawingDelegate.removeFolderBackground(this);
+        }
+
+        mDrawingDelegate = null;
+        isClipping = true;
+        invalidate();
+    }
+
+    boolean drawingDelegated() {
+        return mDrawingDelegate != null;
+    }
+
+    private void animateScale(float finalScale, float finalMultiplier,
+                              final Runnable onStart, final Runnable onEnd) {
+        final float scale0 = mScale;
+        final float scale1 = finalScale;
+
+        final float bgMultiplier0 = mColorMultiplier;
+        final float bgMultiplier1 = finalMultiplier;
+
+        if (mScaleAnimator != null) {
+            mScaleAnimator.cancel();
+        }
+
+        mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+
+        mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float prog = animation.getAnimatedFraction();
+                mScale = prog * scale1 + (1 - prog) * scale0;
+                mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
+                invalidate();
+            }
+        });
+        mScaleAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (onStart != null) {
+                    onStart.run();
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onEnd != null) {
+                    onEnd.run();
+                }
+                mScaleAnimator = null;
+            }
+        });
+
+        mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
+        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 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);
+    }
+
+    public int getBackgroundAlpha() {
+        return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+    }
+
+    public float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewImageView.java b/src/com/android/launcher3/folder/PreviewImageView.java
index c4f3ee1..65d9db1 100644
--- a/src/com/android/launcher3/folder/PreviewImageView.java
+++ b/src/com/android/launcher3/folder/PreviewImageView.java
@@ -22,7 +22,6 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.ImageView;
 
 import com.android.launcher3.Launcher;
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
new file mode 100644
index 0000000..607b7ca
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.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.folder;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Manages the parameters used to draw a Folder preview item.
+ */
+class PreviewItemDrawingParams {
+    float transX;
+    float transY;
+    float scale;
+    float overlayAlpha;
+    FolderPreviewItemAnim anim;
+    public boolean hidden;
+    Drawable drawable;
+
+    PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+        this.overlayAlpha = overlayAlpha;
+    }
+
+    public void update(float transX, float transY, float scale) {
+        // 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) {
+                return;
+            }
+            anim.cancel();
+        }
+
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
new file mode 100644
index 0000000..2d979a6
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -0,0 +1,359 @@
+/*
+ * 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.folder;
+
+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.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
+
+/**
+ * Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}.
+ */
+public class PreviewItemManager {
+
+    private FolderIcon mIcon;
+
+    // These variables are all associated with the drawing of the preview; they are stored
+    // as member variables for shared usage and to avoid computation on each frame
+    private float mIntrinsicIconSize = -1;
+    private int mTotalWidth = -1;
+    private int mPrevTopPadding = -1;
+    private Drawable mReferenceDrawable = null;
+
+    // These hold the first page preview items
+    private ArrayList<PreviewItemDrawingParams> mFirstPageParams = new ArrayList<>();
+    // These hold the current page preview items. It is empty if the current page is the first page.
+    private ArrayList<PreviewItemDrawingParams> mCurrentPageParams = new ArrayList<>();
+
+    private float mCurrentPageItemsTransX = 0;
+    private boolean mShouldSlideInFirstPage;
+
+    static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
+    private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
+
+    private static final int SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION_DELAY = 100;
+    private static final int SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION = 300;
+    private static final int ITEM_SLIDE_IN_OUT_DISTANCE_PX = 200;
+
+    public PreviewItemManager(FolderIcon icon) {
+        mIcon = icon;
+    }
+
+    /**
+     * @param reverse If true, animates the final item in the preview to be full size. If false,
+     *                animates the first item to its position in the preview.
+     */
+    public FolderPreviewItemAnim createFirstItemAnimation(final boolean reverse,
+            final Runnable onCompleteRunnable) {
+        return reverse
+                ? new FolderPreviewItemAnim(this, mFirstPageParams.get(0), 0, 2, -1, -1,
+                        FINAL_ITEM_ANIMATION_DURATION, onCompleteRunnable)
+                : new FolderPreviewItemAnim(this, mFirstPageParams.get(0), -1, -1, 0, 2,
+                        INITIAL_ITEM_ANIMATION_DURATION, onCompleteRunnable);
+    }
+
+    Drawable prepareCreateAnimation(final View destView) {
+        Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+                destView.getMeasuredWidth());
+        mReferenceDrawable = animateDrawable;
+        return animateDrawable;
+    }
+
+    public void recomputePreviewDrawingParams() {
+        if (mReferenceDrawable != null) {
+            computePreviewDrawingParams(mReferenceDrawable.getIntrinsicWidth(),
+                    mIcon.getMeasuredWidth());
+        }
+    }
+
+    private void computePreviewDrawingParams(int drawableSize, int totalSize) {
+        if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize ||
+                mPrevTopPadding != mIcon.getPaddingTop()) {
+            mIntrinsicIconSize = drawableSize;
+            mTotalWidth = totalSize;
+            mPrevTopPadding = mIcon.getPaddingTop();
+
+            mIcon.mBackground.setup(mIcon.mLauncher, mIcon, mTotalWidth, mIcon.getPaddingTop());
+            mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize,
+                    Utilities.isRtl(mIcon.getResources()));
+
+            updateItemDrawingParams(false);
+        }
+    }
+
+    PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
+        // We use an index of -1 to represent an icon on the workspace for the destroy and
+        // create animations
+        if (index == -1) {
+            return getFinalIconParams(params);
+        }
+        return mIcon.mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params);
+    }
+
+    private PreviewItemDrawingParams getFinalIconParams(PreviewItemDrawingParams params) {
+        float iconSize = mIcon.mLauncher.getDeviceProfile().iconSizePx;
+
+        final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
+        final float trans = (mIcon.mBackground.previewSize - iconSize) / 2;
+
+        params.update(trans, trans, scale);
+        return params;
+    }
+
+    public void drawParams(Canvas canvas, ArrayList<PreviewItemDrawingParams> params,
+            float transX) {
+        canvas.translate(transX, 0);
+        // The first item should be drawn last (ie. on top of later items)
+        for (int i = params.size() - 1; i >= 0; i--) {
+            PreviewItemDrawingParams p = params.get(i);
+            if (!p.hidden) {
+                drawPreviewItem(canvas, p);
+            }
+        }
+        canvas.translate(-transX, 0);
+    }
+
+    public void draw(Canvas canvas) {
+        // The items are drawn in coordinates relative to the preview offset
+        PreviewBackground bg = mIcon.getFolderBackground();
+        canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY);
+
+        float firstPageItemsTransX = 0;
+        if (mShouldSlideInFirstPage) {
+            drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
+
+            firstPageItemsTransX = -ITEM_SLIDE_IN_OUT_DISTANCE_PX + mCurrentPageItemsTransX;
+        }
+
+        drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
+        canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY);
+    }
+
+    public void onParamsChanged() {
+        mIcon.invalidate();
+    }
+
+    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
+        canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(params.transX, params.transY);
+        canvas.scale(params.scale, params.scale);
+        Drawable d = params.drawable;
+
+        if (d != null) {
+            Rect bounds = d.getBounds();
+            canvas.save();
+            canvas.translate(-bounds.left, -bounds.top);
+            canvas.scale(mIntrinsicIconSize / bounds.width(), mIntrinsicIconSize / bounds.height());
+            d.draw(canvas);
+            canvas.restore();
+        }
+        canvas.restore();
+    }
+
+    public void hidePreviewItem(int index, boolean hidden) {
+        PreviewItemDrawingParams params = index < mFirstPageParams.size() ?
+                mFirstPageParams.get(index) : null;
+        if (params != null) {
+            params.hidden = hidden;
+        }
+    }
+
+    void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
+        List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page);
+        int prevNumItems = params.size();
+
+        // We adjust the size of the list to match the number of items in the preview.
+        while (items.size() < params.size()) {
+            params.remove(params.size() - 1);
+        }
+        while (items.size() > params.size()) {
+            params.add(new PreviewItemDrawingParams(0, 0, 0, 0));
+        }
+
+        int numItemsInFirstPagePreview = page == 0 ? items.size() : FolderIcon.NUM_ITEMS_IN_PREVIEW;
+        for (int i = 0; i < params.size(); i++) {
+            PreviewItemDrawingParams p = params.get(i);
+            p.drawable = items.get(i).getCompoundDrawables()[1];
+
+            if (p.drawable != null && !mIcon.mFolder.isOpen()) {
+                // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+                // callback will be released when the folder is opened.
+                p.drawable.setCallback(mIcon);
+            }
+
+            if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
+                computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p);
+                if (mReferenceDrawable == null) {
+                    mReferenceDrawable = p.drawable;
+                }
+            } else {
+                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
+                        numItemsInFirstPagePreview, DROP_IN_ANIMATION_DURATION, null);
+
+                if (p.anim != null) {
+                    if (p.anim.hasEqualFinalState(anim)) {
+                        // do nothing, let the current animation finish
+                        continue;
+                    }
+                    p.anim.cancel();
+                }
+                p.anim = anim;
+                p.anim.start();
+            }
+        }
+    }
+
+    void onFolderClose(int currentPage) {
+        // If we are not closing on the first page, we animate the current page preview items
+        // out, and animate the first page preview items in.
+        mShouldSlideInFirstPage = currentPage != 0;
+        if (mShouldSlideInFirstPage) {
+            mCurrentPageItemsTransX = 0;
+            buildParamsForPage(currentPage, mCurrentPageParams, false);
+            onParamsChanged();
+
+            ValueAnimator slideAnimator = ValueAnimator.ofFloat(0, ITEM_SLIDE_IN_OUT_DISTANCE_PX);
+            slideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                    mCurrentPageItemsTransX = (float) valueAnimator.getAnimatedValue();
+                    onParamsChanged();
+                }
+            });
+            slideAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mCurrentPageParams.clear();
+                }
+            });
+            slideAnimator.setStartDelay(SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION_DELAY);
+            slideAnimator.setDuration(SLIDE_IN_FIRST_PAGE_ANIMATION_DURATION);
+            slideAnimator.start();
+        }
+    }
+
+    void updateItemDrawingParams(boolean animate) {
+        buildParamsForPage(0, mFirstPageParams, animate);
+    }
+
+    boolean verifyDrawable(@NonNull Drawable who) {
+        for (int i = 0; i < mFirstPageParams.size(); i++) {
+            if (mFirstPageParams.get(i).drawable == who) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    float getIntrinsicIconSize() {
+        return mIntrinsicIconSize;
+    }
+
+    /**
+     * Handles the case where items in the preview are either:
+     *  - Moving into the preview
+     *  - Moving into a new position
+     *  - Moving out of the preview
+     *
+     * @param oldParams The list of items in the old preview.
+     * @param newParams The list of items in the new preview.
+     * @param dropped The item that was dropped onto the FolderIcon.
+     */
+    public void onDrop(List<BubbleTextView> oldParams, List<BubbleTextView> newParams,
+            ShortcutInfo dropped) {
+        int numItems = newParams.size();
+        final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
+        buildParamsForPage(0, params, false);
+
+        // New preview items for items that are moving in (except for the dropped item).
+        List<BubbleTextView> moveIn = new ArrayList<>();
+        for (BubbleTextView btv : newParams) {
+            if (!oldParams.contains(btv) && !btv.getTag().equals(dropped)) {
+                moveIn.add(btv);
+            }
+        }
+        for (int i = 0; i < moveIn.size(); ++i) {
+            int prevIndex = newParams.indexOf(moveIn.get(i));
+            PreviewItemDrawingParams p = params.get(prevIndex);
+            computePreviewItemDrawingParams(prevIndex, numItems, p);
+            updateTransitionParam(p, moveIn.get(i), mIcon.mPreviewLayoutRule.getEnterIndex(),
+                    newParams.indexOf(moveIn.get(i)));
+        }
+
+        // Items that are moving into new positions within the preview.
+        for (int newIndex = 0; newIndex < newParams.size(); ++newIndex) {
+            int oldIndex = oldParams.indexOf(newParams.get(newIndex));
+            if (oldIndex >= 0 && newIndex != oldIndex) {
+                PreviewItemDrawingParams p = params.get(newIndex);
+                updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex);
+            }
+        }
+
+        // Old preview items that need to be moved out.
+        List<BubbleTextView> moveOut = new ArrayList<>(oldParams);
+        moveOut.removeAll(newParams);
+        for (int i = 0; i < moveOut.size(); ++i) {
+            BubbleTextView item = moveOut.get(i);
+            int oldIndex = oldParams.indexOf(item);
+            PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null);
+            updateTransitionParam(p, item, oldIndex, mIcon.mPreviewLayoutRule.getExitIndex());
+            params.add(0, p); // We want these items first so that they are on drawn last.
+        }
+
+        for (int i = 0; i < params.size(); ++i) {
+            if (params.get(i).anim != null) {
+                params.get(i).anim.start();
+            }
+        }
+    }
+
+    private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv,
+            int prevIndex, int newIndex) {
+        p.drawable = btv.getCompoundDrawables()[1];
+        if (!mIcon.mFolder.isOpen()) {
+            // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+            // callback will be released when the folder is opened.
+            p.drawable.setCallback(mIcon);
+        }
+
+        FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex,
+                FolderIcon.NUM_ITEMS_IN_PREVIEW, newIndex, FolderIcon.NUM_ITEMS_IN_PREVIEW,
+                DROP_IN_ANIMATION_DURATION, null);
+        if (p.anim != null && !p.anim.hasEqualFinalState(anim)) {
+            p.anim.cancel();
+        }
+        p.anim = anim;
+    }
+}
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 297203a..7d10556 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -16,12 +16,6 @@
 
 package com.android.launcher3.folder;
 
-import android.view.View;
-
-import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams;
-
-import java.util.List;
-
 public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
 
     static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;
@@ -39,7 +33,7 @@
     private float mMaxPerspectiveShift;
 
     @Override
-    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+    public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
         mAvailableSpaceInPreview = availableSpace;
 
         // cos(45) = 0.707  + ~= 0.1) = 0.8f
@@ -87,6 +81,11 @@
     }
 
     @Override
+    public float getIconSize() {
+        return mBaselineIconSize;
+    }
+
+    @Override
     public float scaleForItem(int index, int numItems) {
         // Scale is determined by the position of the icon in the preview.
         index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
@@ -100,8 +99,17 @@
     }
 
     @Override
-    public List<View> getItemsToDisplay(Folder folder) {
-        List<View> items = folder.getItemsInReadingOrder();
-        return items.subList(0, Math.min(items.size(), MAX_NUM_ITEMS_IN_PREVIEW));
+    public boolean hasEnterExitIndices() {
+        return false;
+    }
+
+    @Override
+    public int getExitIndex() {
+        throw new RuntimeException("hasEnterExitIndices not supported");
+    }
+
+    @Override
+    public int getEnterIndex() {
+        throw new RuntimeException("hasEnterExitIndices not supported");
     }
 }
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index bb136f7..10e91c0 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -23,13 +23,12 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.view.View;
-import android.widget.TextView;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetHostView;
 import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 
 /**
@@ -57,8 +56,8 @@
         blurSizeOutline =
                 context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
 
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             previewPadding = blurSizeOutline - bounds.left - bounds.top;
         } else {
@@ -71,8 +70,8 @@
      */
     private void drawDragView(Canvas destCanvas) {
         destCanvas.save();
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             destCanvas.translate(blurSizeOutline / 2 - bounds.left,
                     blurSizeOutline / 2 - bounds.top);
@@ -112,8 +111,8 @@
         int width = mView.getWidth();
         int height = mView.getHeight();
 
-        if (mView instanceof TextView) {
-            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+        if (mView instanceof BubbleTextView) {
+            Drawable d = ((BubbleTextView) mView).getIcon();
             Rect bounds = getDrawableBounds(d);
             width = bounds.width();
             height = bounds.height();
@@ -138,7 +137,7 @@
     }
 
     public final void generateDragOutline(Canvas canvas) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
             throw new RuntimeException("Drag outline generated twice");
         }
 
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 60bbce4..371479b 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -28,16 +28,14 @@
 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.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
 
-import java.util.HashMap;
-
 /**
  * Factory for creating new drawables.
  */
@@ -61,7 +59,7 @@
     }
 
     protected final UserHandle mMyUser = Process.myUserHandle();
-    protected final HashMap<UserHandle, Bitmap> mUserBadges = new HashMap<>();
+    protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
 
     /**
      * Returns a FastBitmapDrawable with the icon.
@@ -82,7 +80,7 @@
 
 
     protected Path getPreloadProgressPath(Context context) {
-        if (Utilities.isAtLeastO()) {
+        if (Utilities.ATLEAST_OREO) {
             try {
                 // Try to load the path from Mask Icon
                 Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
diff --git a/src/com/android/launcher3/graphics/FastScrollThumbDrawable.java b/src/com/android/launcher3/graphics/FastScrollThumbDrawable.java
new file mode 100644
index 0000000..6ebc74e
--- /dev/null
+++ b/src/com/android/launcher3/graphics/FastScrollThumbDrawable.java
@@ -0,0 +1,89 @@
+/*
+ * 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.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class FastScrollThumbDrawable extends Drawable {
+
+    private static final Matrix sMatrix = new Matrix();
+
+    private final Path mPath = new Path();
+    private final Paint mPaint;
+    private final boolean mIsRtl;
+
+    public FastScrollThumbDrawable(Paint paint, boolean isRtl) {
+        mPaint = paint;
+        mIsRtl = isRtl;
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        if (mPath.isConvex()) {
+            outline.setConvexPath(mPath);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mPath.reset();
+
+        float r = bounds.height()  * 0.5f;
+        // The path represents a rotate tear-drop shape, with radius of one corner is 1/5th of the
+        // other 3 corners.
+        float diameter = 2 * r;
+        float r2 = r / 5;
+        mPath.addRoundRect(bounds.left, bounds.top, bounds.left + diameter, bounds.top + diameter,
+                new float[] {r, r, r, r, r2, r2, r, r},
+                Path.Direction.CCW);
+
+        sMatrix.setRotate(-45, bounds.left + r, bounds.top + r);
+        if (mIsRtl) {
+            sMatrix.postTranslate(bounds.width(), 0);
+            sMatrix.postScale(-1, 1, bounds.width(), 0);
+        }
+        mPath.transform(sMatrix);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawPath(mPath, mPaint);
+    }
+
+    @Override
+    public void setAlpha(int i) {
+        // Not supported
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        // Not supported
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
new file mode 100644
index 0000000..5455b43
--- /dev/null
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -0,0 +1,198 @@
+/*
+ * 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.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+import com.android.launcher3.util.Themes;
+
+/**
+ * Draws a translucent radial gradient background from an initial state with progress 0.0 to a
+ * final state with progress 1.0;
+ */
+public class GradientView extends View implements WallpaperColorInfo.OnChangeListener {
+
+    private static final int DEFAULT_COLOR = Color.WHITE;
+    private static final int ALPHA_MASK_HEIGHT_DP = 500;
+    private static final int ALPHA_MASK_WIDTH_DP = 2;
+    private static final boolean DEBUG = false;
+
+    private final Bitmap mAlphaGradientMask;
+
+    private boolean mShowScrim = true;
+    private int mColor1 = DEFAULT_COLOR;
+    private int mColor2 = DEFAULT_COLOR;
+    private int mWidth;
+    private int mHeight;
+    private final RectF mAlphaMaskRect = new RectF();
+    private final RectF mFinalMaskRect = new RectF();
+    private final Paint mPaintWithScrim = new Paint();
+    private final Paint mPaintNoScrim = new Paint();
+    private float mProgress;
+    private final int mMaskHeight, mMaskWidth;
+    private final int mAlphaColors;
+    private final Paint mDebugPaint = DEBUG ? new Paint() : null;
+    private final Interpolator mAccelerator = new AccelerateInterpolator();
+    private final float mAlphaStart;
+    private final WallpaperColorInfo mWallpaperColorInfo;
+    private final int mScrimColor;
+
+    public GradientView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
+        this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
+        Launcher launcher = Launcher.getLauncher(context);
+        this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100;
+        this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+        this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
+        mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha);
+        updateColors();
+        mAlphaGradientMask = createDitheredAlphaMask();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mWallpaperColorInfo.addOnChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mWallpaperColorInfo.removeOnChangeListener(this);
+    }
+
+    @Override
+    public void onExtractedColorsChanged(WallpaperColorInfo info) {
+        updateColors();
+        invalidate();
+    }
+
+    private void updateColors() {
+        this.mColor1 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getMainColor(),
+                mAlphaColors);
+        this.mColor2 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getSecondaryColor(),
+                mAlphaColors);
+        if (mWidth + mHeight > 0) {
+            createRadialShader();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        this.mWidth = getMeasuredWidth();
+        this.mHeight = getMeasuredHeight();
+        if (mWidth + mHeight > 0) {
+            createRadialShader();
+        }
+    }
+
+    // only being called when colors change
+    private void createRadialShader() {
+        final float gradientCenterY = 1.05f;
+        float radius = Math.max(mHeight, mWidth) * gradientCenterY;
+        float posScreenBottom = (radius - mHeight) / radius; // center lives below screen
+
+        RadialGradient shaderNoScrim = new RadialGradient(
+                mWidth * 0.5f,
+                mHeight * gradientCenterY,
+                radius,
+                new int[] {mColor1, mColor1, mColor2},
+                new float[] {0f, posScreenBottom, 1f},
+                Shader.TileMode.CLAMP);
+        mPaintNoScrim.setShader(shaderNoScrim);
+
+        int color1 = ColorUtils.compositeColors(mScrimColor,mColor1);
+        int color2 = ColorUtils.compositeColors(mScrimColor,mColor2);
+        RadialGradient shaderWithScrim = new RadialGradient(
+                mWidth * 0.5f,
+                mHeight * gradientCenterY,
+                radius,
+                new int[] { color1, color1, color2 },
+                new float[] {0f, posScreenBottom, 1f},
+                Shader.TileMode.CLAMP);
+        mPaintWithScrim.setShader(shaderWithScrim);
+    }
+
+    public void setProgress(float progress) {
+        setProgress(progress, true);
+    }
+
+    public void setProgress(float progress, boolean showScrim) {
+        this.mProgress = progress;
+        this.mShowScrim = showScrim;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        Paint paint = mShowScrim ? mPaintWithScrim : mPaintNoScrim;
+
+        float head = 0.29f;
+        float linearProgress = head + (mProgress * (1f - head));
+        float startMaskY = (1f - linearProgress) * mHeight - mMaskHeight * linearProgress;
+        float interpolatedAlpha = (255 - mAlphaStart) * mAccelerator.getInterpolation(mProgress);
+        paint.setAlpha((int) (mAlphaStart + interpolatedAlpha));
+        float div = (float) Math.floor(startMaskY + mMaskHeight);
+        mAlphaMaskRect.set(0, startMaskY, mWidth, div);
+        mFinalMaskRect.set(0, div, mWidth, mHeight);
+        canvas.drawBitmap(mAlphaGradientMask, null, mAlphaMaskRect, paint);
+        canvas.drawRect(mFinalMaskRect, paint);
+
+        if (DEBUG) {
+            mDebugPaint.setColor(0xFF00FF00);
+            canvas.drawLine(0, startMaskY, mWidth, startMaskY, mDebugPaint);
+            canvas.drawLine(0, startMaskY + mMaskHeight, mWidth, startMaskY + mMaskHeight, mDebugPaint);
+        }
+    }
+
+    public Bitmap createDitheredAlphaMask() {
+        Bitmap dst = Bitmap.createBitmap(mMaskWidth, mMaskHeight, Bitmap.Config.ALPHA_8);
+        Canvas c = new Canvas(dst);
+        Paint paint = new Paint(Paint.DITHER_FLAG);
+        LinearGradient lg = new LinearGradient(0, 0, 0, mMaskHeight,
+                new int[]{
+                        0x00FFFFFF,
+                        ColorUtils.setAlphaComponent(Color.WHITE, (int) (0xFF * 0.95)),
+                        0xFFFFFFFF},
+                new float[]{0f, 0.8f, 1f},
+                Shader.TileMode.CLAMP);
+        paint.setShader(lg);
+        c.drawRect(0, 0, mMaskWidth, mMaskHeight, paint);
+        return dst;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index c9873d9..b221828 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -31,7 +31,7 @@
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.nio.ByteBuffer;
 
@@ -86,7 +86,7 @@
      * bitmap.
      */
     public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
             throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
         }
 
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 34d0b72..28fc423 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -32,10 +32,8 @@
 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 java.io.File;
 import java.io.FileOutputStream;
 import java.nio.ByteBuffer;
@@ -84,12 +82,12 @@
     private final Rect mBounds;
     private final Matrix mMatrix;
 
-    private Paint mPaintIcon;
-    private Canvas mCanvasARGB;
+    private final Paint mPaintIcon;
+    private final Canvas mCanvasARGB;
 
-    private File mDir;
+    private final File mDir;
     private int mFileId;
-    private Random mRandom;
+    private final Random mRandom;
 
     private IconNormalizer(Context context) {
         // Use twice the icon size as maximum size to avoid scaling down twice.
@@ -122,7 +120,6 @@
         mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
 
         mMatrix = new Matrix();
-        int[] mPixels = new int[mMaxSize * mMaxSize];
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
 
         mDir = context.getExternalFilesDir(null);
@@ -174,7 +171,8 @@
 
         boolean isTrans = isTransparentBitmap(mBitmapARGB);
         if (DEBUG) {
-            final File afterFile = new File(mDir, "isShape" + mFileId + "_after_" + isTrans + ".png");
+            final File afterFile = new File(mDir,
+                    "isShape" + mFileId + "_after_" + isTrans + ".png");
             try {
                 mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100,
                         new FileOutputStream(afterFile));
@@ -210,7 +208,9 @@
         float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
         boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
         if (DEBUG) {
-            Log.d(TAG, "Total # pixel that is different (id="+ mFileId + "):" + percentageDiffPixels + "="+ sum + "/" + mBounds.width() * mBounds.height());
+            Log.d(TAG,
+                    "Total # pixel that is different (id=" + mFileId + "):" + percentageDiffPixels
+                            + "=" + sum + "/" + mBounds.width() * mBounds.height());
         }
         return transparentImage;
     }
@@ -231,7 +231,7 @@
      */
     public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
             @Nullable Path path, @Nullable boolean[] outMaskShape) {
-        if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
+        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
                 mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
             if (outBounds != null) {
                 outBounds.set(mAdaptiveIconBounds);
@@ -347,7 +347,7 @@
         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.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
+        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
                 mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
             mAdaptiveIconScale = scale;
             mAdaptiveIconBounds.set(mBounds);
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index a17ceeb..6e01ed5 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -169,43 +169,40 @@
      * This was copied from com.android.internal.util.NotificationColorUtil.
      */
     private static int ensureTextContrast(int color, int bg) {
-        return findContrastColor(color, bg, true, 4.5);
+        return findContrastColor(color, bg, 4.5);
     }
     /**
      * Finds a suitable color such that there's enough contrast.
      *
-     * @param color the color to start searching from.
-     * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
-     * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+     * @param fg the color to start searching from.
+     * @param bg the color to ensure contrast against.
      * @param minRatio the minimum contrast ratio required.
      * @return a color with the same hue as {@param color}, potentially darkened to meet the
      *          contrast ratio.
      *
      * This was copied from com.android.internal.util.NotificationColorUtil.
      */
-    private static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
-        int fg = findFg ? color : other;
-        int bg = findFg ? other : color;
+    private static int findContrastColor(int fg, int bg, double minRatio) {
         if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
-            return color;
+            return fg;
         }
 
         double[] lab = new double[3];
-        ColorUtils.colorToLAB(findFg ? fg : bg, lab);
+        ColorUtils.colorToLAB(bg, lab);
+        double bgL = lab[0];
+        ColorUtils.colorToLAB(fg, lab);
+        double fgL = lab[0];
+        boolean isBgDark = bgL < 50;
 
-        double low = 0, high = lab[0];
+        double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
         final double a = lab[1], b = lab[2];
         for (int i = 0; i < 15 && high - low > 0.00001; i++) {
             final double l = (low + high) / 2;
-            if (findFg) {
-                fg = ColorUtils.LABToColor(l, a, b);
-            } else {
-                bg = ColorUtils.LABToColor(l, a, b);
-            }
+            fg = ColorUtils.LABToColor(l, a, b);
             if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
-                low = l;
+                if (isBgDark) high = l; else low = l;
             } else {
-                high = l;
+                if (isBgDark) low = l; else high = l;
             }
         }
         return ColorUtils.LABToColor(low, a, b);
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index f78f8a5..223243b 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -15,13 +15,14 @@
  */
 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.SharedPreferences;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.SystemClock;
@@ -34,11 +35,10 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
 
 import java.lang.reflect.Field;
 
@@ -59,7 +59,7 @@
     private static final int RESTART_REQUEST_CODE = 42; // the answer to everything
 
     public static boolean isSupported(Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return false;
         }
         // Only supported when developer settings is enabled
@@ -82,7 +82,7 @@
     }
 
     public static void apply(Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         String path = getAppliedValue(context);
@@ -101,7 +101,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Unable to override icon shape", e);
             // revert value.
-            prefs(context).edit().remove(KEY_PREFERENCE).apply();
+            getDevicePrefs(context).edit().remove(KEY_PREFERENCE).apply();
         }
     }
 
@@ -116,11 +116,7 @@
     }
 
     private static String getAppliedValue(Context context) {
-        return prefs(context).getString(KEY_PREFERENCE, "");
-    }
-
-    private static SharedPreferences prefs(Context context) {
-        return context.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
+        return getDevicePrefs(context).getString(KEY_PREFERENCE, "");
     }
 
     public static void handlePreferenceUi(ListPreference preference) {
@@ -169,7 +165,7 @@
                         mContext.getString(R.string.icon_shape_override_progress),
                         true /* indeterminate */,
                         false /* cancelable */);
-                new LooperExecuter(LauncherModel.getWorkerLooper()).execute(
+                new LooperExecutor(LauncherModel.getWorkerLooper()).execute(
                         new OverrideApplyHandler(mContext, newValue));
             }
             return false;
@@ -189,7 +185,7 @@
         @Override
         public void run() {
             // Synchronously write the preference.
-            prefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
+            getDevicePrefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
             // Clear the icon cache.
             LauncherAppState.getInstance(mContext).getIconCache().clear();
 
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 19e5702..d55baf0 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -35,8 +35,10 @@
 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.LauncherAppState;
 import com.android.launcher3.R;
@@ -45,6 +47,7 @@
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.util.Provider;
 
 /**
  * Helper methods for generating various launcher icons
@@ -101,7 +104,7 @@
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
+            if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
@@ -120,7 +123,7 @@
             }
         }
         Bitmap bitmap = createIconBitmap(icon, context, scale);
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
         }
@@ -155,13 +158,13 @@
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
+            if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
                 dr.setBounds(0, 0, 1, 1);
                 scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape);
-                if (Utilities.isAtLeastO() && FeatureFlags.LEGACY_ICON_TREATMENT &&
+                if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT &&
                         !outShape[0]) {
                     Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
                     if (wrappedIcon != icon) {
@@ -190,13 +193,16 @@
      * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions.
      */
     public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) {
+        return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context);
+    }
+
+    public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
         int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
         synchronized (sCanvas) {
             sCanvas.setBitmap(srcTgt);
-            sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()),
-                    new Rect(srcTgt.getWidth() - badgeSize,
-                            srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()),
-                    new Paint(Paint.FILTER_BITMAP_FLAG));
+            int iconSize = srcTgt.getWidth();
+            badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
+            badge.draw(sCanvas);
             sCanvas.setBitmap(null);
         }
         return srcTgt;
@@ -207,12 +213,12 @@
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context) {
         float scale = 1f;
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0));
         }
         Bitmap bitmap =  createIconBitmap(icon, context, scale);
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
         }
@@ -265,7 +271,7 @@
             final int top = (textureHeight-height) / 2;
 
             sOldBounds.set(icon.getBounds());
-            if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) {
+            if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
                 int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
                         Math.min(left, top));
                 int size = Math.max(width, height);
@@ -290,7 +296,7 @@
      * create AdaptiveIconDrawable.
      */
     static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) {
-        if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.isAtLeastO())) {
+        if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) {
             return drawable;
         }
 
@@ -315,21 +321,50 @@
 
     public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
             boolean badged) {
+        return createShortcutIcon(shortcutInfo, context, badged, null);
+    }
+
+    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+            final Bitmap fallbackIcon) {
+        Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() {
+            @Override
+            public Bitmap get() {
+                // 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.
+                return fallbackIcon;
+            }
+        };
+        return createShortcutIcon(shortcutInfo, context, true, fallbackIconProvider);
+    }
+
+    public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+            boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
         LauncherAppState app = LauncherAppState.getInstance(context);
         Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
                 .getShortcutIconDrawable(shortcutInfo,
                         app.getInvariantDeviceProfile().fillResIconDpi);
         IconCache cache = app.getIconCache();
-        Bitmap unbadgedBitmap = unbadgedDrawable == null
-                ? cache.getDefaultIcon(Process.myUserHandle())
-                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context,
-                Build.VERSION_CODES.O);
+        Bitmap unbadgedBitmap = null;
+        if (unbadgedDrawable != null) {
+            unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
+                    unbadgedDrawable, context, 0);
+        } else {
+            if (fallbackIconProvider != null) {
+                unbadgedBitmap = fallbackIconProvider.get();
+            }
+            if (unbadgedBitmap == null) {
+                unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle());
+            }
+        }
 
         if (!badged) {
             return unbadgedBitmap;
         }
         unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context);
+        return badgeWithBitmap(unbadgedBitmap, getShortcutInfoBadge(shortcutInfo, cache), context);
+    }
 
+    public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
         final Bitmap badgeBitmap;
         ComponentName cn = shortcutInfo.getActivity();
         if (cn != null) {
@@ -347,7 +382,7 @@
             cache.getTitleAndIconForApp(pkgInfo, false);
             badgeBitmap = pkgInfo.iconBitmap;
         }
-        return badgeWithBitmap(unbadgedBitmap, badgeBitmap, context);
+        return badgeBitmap;
     }
 
     /**
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 22ce098..06dc7ac 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -69,7 +69,7 @@
     private static final int COLOR_TRACK = 0x77EEEEEE;
     private static final int COLOR_SHADOW = 0x55000000;
 
-    private static final float SMALL_SCALE = 0.75f;
+    private static final float SMALL_SCALE = 0.6f;
 
     private static final SparseArray<WeakReference<Bitmap>> sShadowCache = new SparseArray<>();
 
@@ -121,11 +121,11 @@
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
         mTmpMatrix.setScale(
-                (bounds.width() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
-                (bounds.height() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
+                (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
+                (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
         mTmpMatrix.postTranslate(
-                bounds.left + PROGRESS_WIDTH / 2 + PROGRESS_GAP,
-                bounds.top + PROGRESS_WIDTH / 2 + PROGRESS_GAP);
+                bounds.left + PROGRESS_WIDTH + PROGRESS_GAP,
+                bounds.top + PROGRESS_WIDTH + PROGRESS_GAP);
 
         mProgressPath.transform(mTmpMatrix, mScaledTrackPath);
         float scale = bounds.width() / PATH_SIZE;
@@ -178,7 +178,7 @@
         Rect bounds = getBounds();
 
         canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
-        drawInternal(canvas);
+        super.draw(canvas);
         canvas.restoreToCount(saveCount);
     }
 
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
new file mode 100644
index 0000000..b40bf78
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShadowDrawable.java
@@ -0,0 +1,214 @@
+/*
+ * 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.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * A drawable which adds shadow around a child drawable.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class ShadowDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+
+    private final ShadowDrawableState mState;
+
+    @SuppressWarnings("unused")
+    public ShadowDrawable() {
+        this(new ShadowDrawableState());
+    }
+
+    private ShadowDrawable(ShadowDrawableState state) {
+        mState = state;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        Rect bounds = getBounds();
+        if (bounds.isEmpty()) {
+            return;
+        }
+        if (mState.mLastDrawnBitmap == null) {
+            regenerateBitmapCache();
+        }
+        canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mState;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mState.mIntrinsicHeight;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mState.mIntrinsicWidth;
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return mState.canApplyTheme();
+    }
+
+    @Override
+    public void applyTheme(Resources.Theme t) {
+        TypedArray ta = t.obtainStyledAttributes(new int[] {R.attr.isWorkspaceDarkText});
+        boolean isDark = ta.getBoolean(0, false);
+        ta.recycle();
+        if (mState.mIsDark != isDark) {
+            mState.mIsDark = isDark;
+            mState.mLastDrawnBitmap = null;
+            invalidateSelf();
+        }
+    }
+
+    private void regenerateBitmapCache() {
+        Bitmap bitmap = Bitmap.createBitmap(mState.mIntrinsicWidth, mState.mIntrinsicHeight,
+                Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+
+        // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+        Drawable d = mState.mChildState.newDrawable().mutate();
+        d.setBounds(mState.mShadowSize, mState.mShadowSize,
+                mState.mIntrinsicWidth - mState.mShadowSize,
+                mState.mIntrinsicHeight - mState.mShadowSize);
+        d.setTint(mState.mIsDark ? mState.mDarkTintColor : Color.WHITE);
+        d.draw(canvas);
+
+        // Do not draw shadow on dark theme
+        if (!mState.mIsDark) {
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+            paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, BlurMaskFilter.Blur.NORMAL));
+            int[] offset = new int[2];
+            Bitmap shadow = bitmap.extractAlpha(paint, offset);
+
+            paint.setMaskFilter(null);
+            paint.setColor(mState.mShadowColor);
+            bitmap.eraseColor(Color.TRANSPARENT);
+            canvas.drawBitmap(shadow, offset[0], offset[1], paint);
+            d.draw(canvas);
+        }
+
+        if (Utilities.ATLEAST_OREO) {
+            bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+        }
+        mState.mLastDrawnBitmap = bitmap;
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs,
+            Resources.Theme theme) throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+
+        final TypedArray a = theme == null
+                ? r.obtainAttributes(attrs, R.styleable.ShadowDrawable)
+                : theme.obtainStyledAttributes(attrs, R.styleable.ShadowDrawable, 0, 0);
+        try {
+            Drawable d = a.getDrawable(R.styleable.ShadowDrawable_android_src);
+            if (d == null) {
+                throw new XmlPullParserException("missing src attribute");
+            }
+            mState.mShadowColor = a.getColor(
+                    R.styleable.ShadowDrawable_android_shadowColor, Color.BLACK);
+            mState.mShadowSize = a.getDimensionPixelSize(
+                    R.styleable.ShadowDrawable_android_elevation, 0);
+            mState.mDarkTintColor = a.getColor(
+                    R.styleable.ShadowDrawable_darkTintColor, Color.BLACK);
+
+            mState.mIntrinsicHeight = d.getIntrinsicHeight() + 2 * mState.mShadowSize;
+            mState.mIntrinsicWidth = d.getIntrinsicWidth() + 2 * mState.mShadowSize;
+            mState.mChangingConfigurations = d.getChangingConfigurations();
+
+            mState.mChildState = d.getConstantState();
+        } finally {
+            a.recycle();
+        }
+    }
+
+    private static class ShadowDrawableState extends ConstantState {
+
+        int mChangingConfigurations;
+        int mIntrinsicWidth;
+        int mIntrinsicHeight;
+
+        int mShadowColor;
+        int mShadowSize;
+        int mDarkTintColor;
+
+        boolean mIsDark;
+        Bitmap mLastDrawnBitmap;
+        ConstantState mChildState;
+
+        @Override
+        public Drawable newDrawable() {
+            return new ShadowDrawable(this);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mChangingConfigurations;
+        }
+
+        @Override
+        public boolean canApplyTheme() {
+            return true;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 5d8cca8..60eeef5 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -22,11 +22,12 @@
 import android.graphics.BlurMaskFilter;
 import android.graphics.BlurMaskFilter.Blur;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.support.v4.graphics.ColorUtils;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.util.Preconditions;
 
 /**
  * Utility class to add shadows to bitmaps.
@@ -38,10 +39,10 @@
     public static final float BLUR_FACTOR = 0.5f/48;
 
     // Percent of actual icon size
-    private static final float KEY_SHADOW_DISTANCE = 1f/48;
-    public static final int KEY_SHADOW_ALPHA = 61;
+    public static final float KEY_SHADOW_DISTANCE = 1f/48;
+    private static final int KEY_SHADOW_ALPHA = 61;
 
-    public static final int AMBIENT_SHADOW_ALPHA = 30;
+    private static final int AMBIENT_SHADOW_ALPHA = 30;
 
     private static final Object LOCK = new Object();
     // Singleton object guarded by {@link #LOCK}
@@ -52,27 +53,38 @@
     private final Canvas mCanvas;
     private final Paint mBlurPaint;
     private final Paint mDrawPaint;
+    private final BlurMaskFilter mDefaultBlurMaskFilter;
 
     private ShadowGenerator(Context context) {
         mIconSize = LauncherAppState.getIDP(context).iconBitmapSize;
         mCanvas = new Canvas();
         mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        mBlurPaint.setMaskFilter(new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL));
         mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL);
     }
 
     public synchronized Bitmap recreateIcon(Bitmap icon) {
+        return recreateIcon(icon, true, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA,
+                KEY_SHADOW_ALPHA);
+    }
+
+    public synchronized Bitmap recreateIcon(Bitmap icon, boolean resize,
+            BlurMaskFilter blurMaskFilter, int ambientAlpha, int keyAlpha) {
+        int width = resize ? mIconSize : icon.getWidth();
+        int height = resize ? mIconSize : icon.getHeight();
         int[] offset = new int[2];
+
+        mBlurPaint.setMaskFilter(blurMaskFilter);
         Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
-        Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Config.ARGB_8888);
+        Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
         mCanvas.setBitmap(result);
 
         // Draw ambient shadow
-        mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
+        mDrawPaint.setAlpha(ambientAlpha);
         mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
 
         // Draw key shadow
-        mDrawPaint.setAlpha(KEY_SHADOW_ALPHA);
+        mDrawPaint.setAlpha(keyAlpha);
         mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
 
         // Draw the icon
@@ -83,48 +95,10 @@
         return result;
     }
 
-    public static Bitmap createPillWithShadow(int rectColor, int width, int height) {
-
-        float shadowRadius = height * 1f / 32;
-        float shadowYOffset = height * 1f / 16;
-
-        int radius = height / 2;
-
-        Canvas canvas = new Canvas();
-        Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        blurPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, Blur.NORMAL));
-
-        int centerX = Math.round(width / 2 + shadowRadius);
-        int centerY = Math.round(radius + shadowRadius + shadowYOffset);
-        int center = Math.max(centerX, centerY);
-        int size = center * 2;
-        Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
-        canvas.setBitmap(result);
-
-        int left = center - width / 2;
-        int top = center - height / 2;
-        int right = center + width / 2;
-        int bottom = center + height / 2;
-
-        // Draw ambient shadow, center aligned within size
-        blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
-        canvas.drawRoundRect(left, top, right, bottom, radius, radius, blurPaint);
-
-        // Draw key shadow, bottom aligned within size
-        blurPaint.setAlpha(KEY_SHADOW_ALPHA);
-        canvas.drawRoundRect(left, top + shadowYOffset, right, bottom + shadowYOffset,
-                radius, radius, blurPaint);
-
-        // Draw the circle
-        Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        drawPaint.setColor(rectColor);
-        canvas.drawRoundRect(left, top, right, bottom, radius, radius, drawPaint);
-
-        return result;
-    }
-
     public static ShadowGenerator getInstance(Context context) {
-        Preconditions.assertNonUiThread();
+        // TODO: This currently fails as the system default icon also needs a shadow as it
+        // uses adaptive icon.
+        // Preconditions.assertNonUiThread();
         synchronized (LOCK) {
             if (sShadowGenerator == null) {
                 sShadowGenerator = new ShadowGenerator(context);
@@ -152,4 +126,58 @@
         }
         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 / 2;
+
+            int centerX = Math.round(width / 2 + shadowBlur);
+            int centerY = Math.round(radius + shadowBlur + keyShadowDistance);
+            int center = Math.max(centerX, centerY);
+            bounds.set(0, 0, width, height);
+            bounds.offsetTo(center - width / 2, center - height / 2);
+
+            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);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
index f72ce03..d719575 100644
--- a/src/com/android/launcher3/graphics/TintedDrawableSpan.java
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -34,6 +34,7 @@
         super(ALIGN_BOTTOM);
         mDrawable = context.getDrawable(resourceId);
         mOldTint = 0;
+        mDrawable.setTint(0);
     }
 
     @Override
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index bb0b58a..938955c 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -24,10 +24,10 @@
 import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 
-import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index ffb41b7..f7f8ef1 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -6,9 +6,8 @@
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -30,6 +29,8 @@
  */
 public final class FileLog {
 
+    protected static final boolean ENABLED =
+            FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE;
     private static final String FILE_NAME_PREFIX = "log-";
     private static final DateFormat DATE_FORMAT =
             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
@@ -40,7 +41,7 @@
     private static File sLogsDirectory = null;
 
     public static void setDir(File logsDir) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
+        if (ENABLED) {
             synchronized (DATE_FORMAT) {
                 // If the target directory changes, stop any active thread.
                 if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
@@ -77,7 +78,7 @@
     }
 
     public static void print(String tag, String msg, Exception e) {
-        if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (!ENABLED) {
             return;
         }
         String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
@@ -103,7 +104,7 @@
      * @param out if not null, all the persisted logs are copied to the writer.
      */
     public static void flushAll(PrintWriter out) throws InterruptedException {
-        if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (!ENABLED) {
             return;
         }
         CountDownLatch latch = new CountDownLatch(1);
@@ -136,7 +137,7 @@
 
         @Override
         public boolean handleMessage(Message msg) {
-            if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) {
+            if (sLogsDirectory == null || !ENABLED) {
                 return true;
             }
             switch (msg.what) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 499fdc7..81333b1 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.logging;
 
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.view.View;
@@ -26,6 +25,7 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.UninstallDropTarget;
+import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
@@ -67,8 +67,14 @@
     }
 
     public static String getActionStr(Action action) {
+        String str = "";
         switch (action.type) {
-            case Action.Type.TOUCH: return getFieldName(action.touch, Action.Touch.class);
+            case Action.Type.TOUCH:
+                str += getFieldName(action.touch, Action.Touch.class);
+                if (action.touch == Action.Touch.SWIPE) {
+                    str += " direction=" + getFieldName(action.dir, Action.Direction.class);
+                }
+                return str;
             case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
             default: return UNKNOWN;
         }
@@ -99,13 +105,13 @@
     private static String getItemStr(Target t) {
         String typeStr = getFieldName(t.itemType, ItemType.class);
         if (t.packageNameHash != 0) {
-            typeStr += ", packageHash=" + t.packageNameHash;
+            typeStr += ", packageHash=" + t.packageNameHash + ", predictiveRank=" + t.predictedRank;
         }
         if (t.componentHash != 0) {
-            typeStr += ", componentHash=" + t.componentHash;
+            typeStr += ", componentHash=" + t.componentHash + ", predictiveRank=" + t.predictedRank;
         }
         if (t.intentHash != 0) {
-            typeStr += ", intentHash=" + t.intentHash;
+            typeStr += ", intentHash=" + t.intentHash + ", predictiveRank=" + t.predictedRank;
         }
         return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
                 + "), pageIdx=" + t.pageIndex;
@@ -119,9 +125,11 @@
 
     public static Target newItemTarget(ItemInfo info) {
         Target t = newTarget(Target.Type.ITEM);
+
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 t.itemType = ItemType.APP_ICON;
+                t.predictedRank = -100; // Never assigned
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 t.itemType = ItemType.SHORTCUT;
@@ -154,6 +162,13 @@
         return t;
     }
 
+    public static Target newTarget(int targetType, TargetExtension extension) {
+        Target t = new Target();
+        t.type = targetType;
+        t.extension = extension;
+        return t;
+    }
+
     public static Target newTarget(int targetType) {
         Target t = new Target();
         t.type = targetType;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 258af16..d5c6515 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -20,7 +20,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 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;
@@ -29,16 +31,15 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 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.LogConfig;
 
-import java.util.List;
 import java.util.Locale;
+import java.util.UUID;
 
 import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
@@ -60,14 +61,22 @@
 
     private static final String TAG = "UserEvent";
     private static final boolean IS_VERBOSE =
-            ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+            FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+    private static final String UUID_STORAGE = "uuid";
 
     public static UserEventDispatcher newInstance(Context context, boolean isInLandscapeMode,
             boolean isInMultiWindowMode) {
+        SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
+        String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
+        if (uuidStr == null) {
+            uuidStr = UUID.randomUUID().toString();
+            sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
+        }
         UserEventDispatcher ued = Utilities.getOverrideObject(UserEventDispatcher.class,
                 context.getApplicationContext(), R.string.user_event_dispatcher_class);
         ued.mIsInLandscapeMode = isInLandscapeMode;
         ued.mIsInMultiWindowMode = isInMultiWindowMode;
+        ued.mUuidStr = uuidStr;
         return ued;
     }
 
@@ -90,7 +99,7 @@
     /**
      * Recursively finds the parent of the given child which implements IconLogInfoProvider
      */
-    public static LogContainerProvider getLaunchProviderRecursive(View v) {
+    public static LogContainerProvider getLaunchProviderRecursive(@Nullable View v) {
         ViewParent parent;
         if (v != null) {
             parent = v.getParent();
@@ -115,9 +124,7 @@
     private long mActionDurationMillis;
     private boolean mIsInMultiWindowMode;
     private boolean mIsInLandscapeMode;
-
-    // Used for filling in predictedRank on {@link Target}s.
-    private List<ComponentKey> mPredictedApps;
+    private String mUuidStr;
 
     //                      APP_ICON    SHORTCUT    WIDGET
     // --------------------------------------------------------------
@@ -126,28 +133,11 @@
     // intentHash                       required
     // --------------------------------------------------------------
 
-    protected LauncherEvent createLauncherEvent(View v, int intentHashCode, ComponentName cn) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
-                newItemTarget(v), newTarget(Target.Type.CONTAINER));
-
-        // TODO: make idx percolate up the view hierarchy if needed.
-        int idx = 0;
-        if (fillInLogContainerData(event, v)) {
-            ItemInfo itemInfo = (ItemInfo) v.getTag();
-            event.srcTarget[idx].intentHash = intentHashCode;
-            if (cn != null) {
-                event.srcTarget[idx].packageNameHash = cn.getPackageName().hashCode();
-                event.srcTarget[idx].componentHash = cn.hashCode();
-                if (mPredictedApps != null) {
-                    event.srcTarget[idx].predictedRank = mPredictedApps.indexOf(
-                            new ComponentKey(cn, itemInfo.user));
-                }
-            }
-        }
-        return event;
-    }
-
-    public boolean fillInLogContainerData(LauncherEvent event, View v) {
+    /**
+     * 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) {
@@ -159,20 +149,31 @@
     }
 
     public void logAppLaunch(View v, Intent intent) {
-        LauncherEvent ev = createLauncherEvent(v, intent.hashCode(), intent.getComponent());
-        if (ev == null) {
-            return;
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
+                newItemTarget(v), newTarget(Target.Type.CONTAINER));
+
+        if (fillInLogContainerData(event, v)) {
+            fillIntentInfo(event.srcTarget[0], intent);
         }
-        dispatchUserEvent(ev, intent);
+        dispatchUserEvent(event, intent);
+    }
+
+    protected void fillIntentInfo(Target target, Intent intent) {
+        target.intentHash = intent.hashCode();
+        ComponentName cn = intent.getComponent();
+        if (cn != null) {
+            target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode();
+            target.componentHash = (mUuidStr + cn.flattenToString()).hashCode();
+        }
     }
 
     public void logNotificationLaunch(View v, PendingIntent intent) {
-        ComponentName dummyComponent = new ComponentName(intent.getCreatorPackage(), "--dummy--");
-        LauncherEvent ev = createLauncherEvent(v, intent.hashCode(), dummyComponent);
-        if (ev == null) {
-            return;
+        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
+                newItemTarget(v), newTarget(Target.Type.CONTAINER));
+        if (fillInLogContainerData(event, v)) {
+            event.srcTarget[0].packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
         }
-        dispatchUserEvent(ev, null);
+        dispatchUserEvent(event, null);
     }
 
     public void logActionCommand(int command, int containerType) {
@@ -203,9 +204,16 @@
     }
 
     public void logActionOnControl(int action, int controlType) {
-        LauncherEvent event = newLauncherEvent(
-                newTouchAction(action), newTarget(Target.Type.CONTROL));
+        logActionOnControl(action, controlType, null);
+    }
+
+    public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
+        final LauncherEvent event = controlInContainer == null
+                ? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
+                : newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
+                        newTarget(Target.Type.CONTAINER));
         event.srcTarget[0].controlType = controlType;
+        fillInLogContainerData(event, controlInContainer);
         dispatchUserEvent(event, null);
     }
 
@@ -250,10 +258,6 @@
         resetElapsedContainerMillis();
     }
 
-    public void setPredictedApps(List<ComponentKey> predictedApps) {
-        mPredictedApps = predictedApps;
-    }
-
     /* 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() {
@@ -325,7 +329,10 @@
     }
 
     private static String getTargetsStr(Target[] targets) {
-        return "child:" + LoggerUtils.getTargetStr(targets[0]) +
-                (targets.length > 1 ? "\tparent:" + LoggerUtils.getTargetStr(targets[1]) : "");
+        String result = "child:" + LoggerUtils.getTargetStr(targets[0]);
+        for (int i = 1; i < targets.length; i++) {
+            result += "\tparent:" + LoggerUtils.getTargetStr(targets[i]);
+        }
+        return result;
     }
 }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 9696054..42926fa 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -17,10 +17,12 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.LongSparseArray;
 import android.util.Pair;
-
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
@@ -33,29 +35,30 @@
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
 import com.android.launcher3.util.Provider;
-
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Task to add auto-created workspace items.
  */
-public class AddWorkspaceItemsTask extends ExtendedModelTask {
+public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
 
-    private final Provider<List<ItemInfo>> mAppsProvider;
+    private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider;
 
     /**
      * @param appsProvider items to add on the workspace
      */
-    public AddWorkspaceItemsTask(Provider<List<ItemInfo>> appsProvider) {
+    public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
         mAppsProvider = appsProvider;
     }
 
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-        List<ItemInfo> workspaceApps = mAppsProvider.get();
+        List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get();
         if (workspaceApps.isEmpty()) {
             return;
         }
@@ -63,13 +66,17 @@
 
         final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
         final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>();
+        ArrayMap<UserHandle, UserFolderInfo> userFolderMap = new ArrayMap<>();
 
         // 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) {
-            for (ItemInfo item : workspaceApps) {
+
+            List<ItemInfo> filteredItems = new ArrayList<>();
+            for (Pair<ItemInfo, Object> entry : workspaceApps) {
+                ItemInfo item = entry.first;
                 if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                         item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                     // Short-circuit this logic if the icon exists somewhere on the workspace
@@ -78,6 +85,32 @@
                     }
                 }
 
+                if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                    if (item instanceof AppInfo) {
+                        item = ((AppInfo) item).makeShortcut();
+                    }
+
+                    if (!Process.myUserHandle().equals(item.user)) {
+                        // Check if this belongs to a work folder.
+                        if (!(entry.second instanceof LauncherActivityInfo)) {
+                            continue;
+                        }
+
+                        UserFolderInfo userFolderInfo = userFolderMap.get(item.user);
+                        if (userFolderInfo == null) {
+                            userFolderInfo = new UserFolderInfo(context, item.user, dataModel);
+                            userFolderMap.put(item.user, userFolderInfo);
+                        }
+                        item = userFolderInfo.convertToWorkspaceItem(
+                                (ShortcutInfo) item, (LauncherActivityInfo) entry.second);
+                    }
+                }
+                if (item != null) {
+                    filteredItems.add(item);
+                }
+            }
+
+            for (ItemInfo item : filteredItems) {
                 // Find appropriate space for the item.
                 Pair<Long, int[]> coords = findSpaceForItem(app, dataModel, workspaceScreens,
                         addedWorkspaceScreensFinal, item.spanX, item.spanY);
@@ -111,8 +144,8 @@
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
-                    final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
+                    final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
+                    final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
                     if (!addedItemsFinal.isEmpty()) {
                         ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
                         long lastScreenId = info.screenId;
@@ -125,10 +158,14 @@
                         }
                     }
                     callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
-                            addNotAnimated, addAnimated, null);
+                            addNotAnimated, addAnimated);
                 }
             });
         }
+
+        for (UserFolderInfo userFolderInfo : userFolderMap.values()) {
+            userFolderInfo.applyPendingState(getModelWriter());
+        }
     }
 
     protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
@@ -140,7 +177,7 @@
      * the workspace has been loaded. We identify a shortcut by its intent.
      */
     protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
-        final String intentWithPkg, intentWithoutPkg;
+        final String compPkgName, intentWithPkg, intentWithoutPkg;
         if (intent == null) {
             // Skip items with null intents
             return true;
@@ -148,19 +185,21 @@
         if (intent.getComponent() != null) {
             // If component is not null, an intent with null package will produce
             // the same result and should also be a match.
-            String packageName = intent.getComponent().getPackageName();
+            compPkgName = intent.getComponent().getPackageName();
             if (intent.getPackage() != null) {
                 intentWithPkg = intent.toUri(0);
                 intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
             } else {
-                intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
+                intentWithPkg = new Intent(intent).setPackage(compPkgName).toUri(0);
                 intentWithoutPkg = intent.toUri(0);
             }
         } else {
+            compPkgName = null;
             intentWithPkg = intent.toUri(0);
             intentWithoutPkg = intent.toUri(0);
         }
 
+        boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
         synchronized (dataModel) {
             for (ItemInfo item : dataModel.itemsIdMap) {
                 if (item instanceof ShortcutInfo) {
@@ -172,6 +211,16 @@
                         if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
                             return true;
                         }
+
+                        // checking for existing promise icon with same package name
+                        if (isLauncherAppTarget
+                                && info.isPromise()
+                                && info.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)
+                                && info.getTargetComponent() != null
+                                && compPkgName != null
+                                && compPkgName.equals(info.getTargetComponent().getPackageName())) {
+                            return true;
+                        }
                     }
                 }
             }
@@ -263,4 +312,5 @@
         }
         return occupied.findVacantCell(xy, spanX, spanY);
     }
+
 }
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
new file mode 100644
index 0000000..d5b5aa7
--- /dev/null
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -0,0 +1,141 @@
+/*
+ * 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.model;
+
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+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.util.ComponentKey;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Extension of {@link ModelUpdateTask} with some utility methods
+ */
+public abstract class BaseModelUpdateTask implements ModelUpdateTask {
+
+    private static final boolean DEBUG_TASKS = false;
+    private static final String TAG = "BaseModelUpdateTask";
+
+    private LauncherAppState mApp;
+    private LauncherModel mModel;
+    private BgDataModel mDataModel;
+    private AllAppsList mAllAppsList;
+    private Executor mUiExecutor;
+
+    public void init(LauncherAppState app, LauncherModel model,
+            BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor) {
+        mApp = app;
+        mModel = model;
+        mDataModel = dataModel;
+        mAllAppsList = allAppsList;
+        mUiExecutor = uiExecutor;
+    }
+
+    @Override
+    public final void run() {
+        if (!mModel.isModelLoaded()) {
+            if (DEBUG_TASKS) {
+                Log.d(TAG, "Ignoring model task since loader is pending=" + this);
+            }
+            // Loader has not yet run.
+            return;
+        }
+        execute(mApp, mDataModel, mAllAppsList);
+    }
+
+    /**
+     * Execute the actual task. Called on the worker thread.
+     */
+    public abstract void execute(
+            LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+
+    /**
+     * Schedules a {@param task} to be executed on the current callbacks.
+     */
+    public final void scheduleCallbackTask(final CallbackTask task) {
+        final Callbacks callbacks = mModel.getCallback();
+        mUiExecutor.execute(new Runnable() {
+            public void run() {
+                Callbacks cb = mModel.getCallback();
+                if (callbacks == cb && cb != null) {
+                    task.execute(callbacks);
+                }
+            }
+        });
+    }
+
+    public ModelWriter getModelWriter() {
+        // Updates from model task, do not deal with icon position in hotseat.
+        return mModel.getWriter(false /* hasVerticalHotseat */);
+    }
+
+
+    public void bindUpdatedShortcuts(
+            final ArrayList<ShortcutInfo> updatedShortcuts, final UserHandle user) {
+        if (!updatedShortcuts.isEmpty()) {
+            scheduleCallbackTask(new CallbackTask() {
+                @Override
+                public void execute(Callbacks callbacks) {
+                    callbacks.bindShortcutsChanged(updatedShortcuts, user);
+                }
+            });
+        }
+    }
+
+    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);
+            }
+        });
+    }
+
+    public void bindUpdatedWidgets(BgDataModel dataModel) {
+        final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+                = dataModel.widgetsModel.getWidgetsMap();
+        scheduleCallbackTask(new CallbackTask() {
+            @Override
+            public void execute(Callbacks callbacks) {
+                callbacks.bindAllWidgets(widgets);
+            }
+        });
+    }
+
+    public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
+        getModelWriter().deleteItemsFromDatabase(matcher);
+
+        // Call the components-removed callback
+        scheduleCallbackTask(new CallbackTask() {
+            @Override
+            public void execute(Callbacks callbacks) {
+                callbacks.bindWorkspaceComponentsRemoved(matcher);
+            }
+        });
+    }
+}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 0e73ca6..816c1d4 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -27,16 +27,14 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.DumpTargetWrapper;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
 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.model.nano.LauncherDumpProto.ItemType;
+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;
@@ -93,11 +91,21 @@
     public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
 
     /**
+     * True if the launcher has permission to access deep shortcuts.
+     */
+    public boolean hasShortcutHostPermission;
+
+    /**
      * Maps all launcher activities to the id's of their shortcuts (if they have any).
      */
     public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
 
     /**
+     * Entire list of widgets.
+     */
+    public final WidgetsModel widgetsModel = new WidgetsModel();
+
+    /**
      * Clears all the data
      */
     public synchronized void clear() {
@@ -144,7 +152,7 @@
             for (ArrayList<String> map : deepShortcutMap.values()) {
                 writer.print(prefix + "  ");
                 for (String str : map) {
-                    writer.print(str.toString() + ", ");
+                    writer.print(str + ", ");
                 }
                 writer.println();
             }
@@ -158,7 +166,7 @@
         DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
         LongArrayMap<DumpTargetWrapper> workspaces = new LongArrayMap<>();
         for (int i = 0; i < workspaceScreens.size(); i++) {
-            workspaces.put(new Long(workspaceScreens.get(i)),
+            workspaces.put(workspaceScreens.get(i),
                     new DumpTargetWrapper(ContainerType.WORKSPACE, i));
         }
         DumpTargetWrapper dtw;
@@ -175,7 +183,7 @@
             if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                 hotseat.add(dtw);
             } else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                workspaces.get(new Long(fInfo.screenId)).add(dtw);
+                workspaces.get(fInfo.screenId).add(dtw);
             }
         }
         // Add leaf nodes (L3): *Info
@@ -189,7 +197,7 @@
             if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                 hotseat.add(dtw);
             } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                workspaces.get(new Long(info.screenId)).add(dtw);
+                workspaces.get(info.screenId).add(dtw);
             }
         }
         for (int i = 0; i < appWidgets.size(); i++) {
@@ -199,7 +207,7 @@
             if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                 hotseat.add(dtw);
             } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                workspaces.get(new Long(info.screenId)).add(dtw);
+                workspaces.get(info.screenId).add(dtw);
             }
         }
 
@@ -242,7 +250,7 @@
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                     folders.remove(item.id);
-                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                    if (FeatureFlags.IS_DOGFOOD_BUILD) {
                         for (ItemInfo info : itemsIdMap) {
                             if (info.container == item.id) {
                                 // We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 46130fc..0139bd9 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -26,7 +26,6 @@
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.ShortcutInfo;
 
 import java.util.ArrayList;
@@ -35,7 +34,7 @@
 /**
  * Handles changes due to cache updates.
  */
-public class CacheDataUpdatedTask extends ExtendedModelTask {
+public class CacheDataUpdatedTask extends BaseModelUpdateTask {
 
     public static final int OP_CACHE_UPDATE = 1;
     public static final int OP_SESSION_UPDATE = 2;
@@ -78,7 +77,7 @@
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    callbacks.bindAppsUpdated(updatedApps);
+                    callbacks.bindAppsAddedOrUpdated(updatedApps);
                 }
             });
         }
@@ -89,7 +88,7 @@
             case OP_CACHE_UPDATE:
                 return true;
             case OP_SESSION_UPDATE:
-                return si.isPromise();
+                return si.hasPromiseIconUi();
             default:
                 return false;
         }
diff --git a/src/com/android/launcher3/model/DbDowngradeHelper.java b/src/com/android/launcher3/model/DbDowngradeHelper.java
new file mode 100644
index 0000000..cd86b72
--- /dev/null
+++ b/src/com/android/launcher3/model/DbDowngradeHelper.java
@@ -0,0 +1,108 @@
+/*
+ * 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.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.IOUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Utility class to handle DB downgrade
+ */
+public class DbDowngradeHelper {
+
+    private static final String TAG = "DbDowngradeHelper";
+
+    private static final String KEY_VERSION = "version";
+    private static final String KEY_DOWNGRADE_TO = "downgrade_to_";
+
+    private final SparseArray<String[]> mStatements = new SparseArray<>();
+    public final int version;
+
+    private DbDowngradeHelper(int version) {
+        this.version = version;
+    }
+
+    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        ArrayList<String> allCommands = new ArrayList<>();
+
+        for (int i = oldVersion - 1; i >= newVersion; i--) {
+            String[] commands = mStatements.get(i);
+            if (commands == null) {
+                throw new SQLiteException("Downgrade path not supported to version " + i);
+            }
+            Collections.addAll(allCommands, commands);
+        }
+
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+            for (String sql : allCommands) {
+                db.execSQL(sql);
+            }
+            t.commit();
+        }
+    }
+
+    public static DbDowngradeHelper parse(File file) throws JSONException, IOException {
+        JSONObject obj = new JSONObject(new String(IOUtils.toByteArray(file)));
+        DbDowngradeHelper helper = new DbDowngradeHelper(obj.getInt(KEY_VERSION));
+        for (int version = helper.version - 1; version > 0; version--) {
+            if (obj.has(KEY_DOWNGRADE_TO + version)) {
+                JSONArray statements = obj.getJSONArray(KEY_DOWNGRADE_TO + version);
+                String[] parsed = new String[statements.length()];
+                for (int i = 0; i < parsed.length; i++) {
+                    parsed[i] = statements.getString(i);
+                }
+                helper.mStatements.put(version, parsed);
+            }
+        }
+        return helper;
+    }
+
+    public static void updateSchemaFile(File schemaFile, int expectedVersion,
+            Context context, int schemaResId) {
+        try {
+            if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
+                return;
+            }
+        } catch (Exception e) {
+            // Schema error
+        }
+
+        // Write the updated schema
+        try (FileOutputStream fos = new FileOutputStream(schemaFile);
+            InputStream in = context.getResources().openRawResource(schemaResId)) {
+            IOUtils.copy(in, fos);
+        } catch (IOException e) {
+            Log.e(TAG, "Error writing schema file", e);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/ExtendedModelTask.java b/src/com/android/launcher3/model/ExtendedModelTask.java
deleted file mode 100644
index 0541966..0000000
--- a/src/com/android/launcher3/model/ExtendedModelTask.java
+++ /dev/null
@@ -1,62 +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.model;
-
-import android.os.UserHandle;
-
-import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MultiHashMap;
-
-import java.util.ArrayList;
-
-/**
- * Extension of {@link BaseModelUpdateTask} with some utility methods
- */
-public abstract class ExtendedModelTask extends BaseModelUpdateTask {
-
-    public void bindUpdatedShortcuts(
-            ArrayList<ShortcutInfo> updatedShortcuts, UserHandle user) {
-        bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user);
-    }
-
-    public void bindUpdatedShortcuts(
-            final ArrayList<ShortcutInfo> updatedShortcuts,
-            final ArrayList<ShortcutInfo> removedShortcuts,
-            final UserHandle user) {
-        if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
-                }
-            });
-        }
-    }
-
-    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);
-            }
-        });
-    }
-}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 221798b..8de0de0 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -13,7 +13,6 @@
 import android.net.Uri;
 import android.text.TextUtils;
 import android.util.Log;
-
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
@@ -29,10 +28,8 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
-
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
 
@@ -61,7 +58,6 @@
     private final Context mContext;
     private final InvariantDeviceProfile mIdp;
 
-    private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
     private final ContentValues mTempValues = new ContentValues();
     protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
     private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
@@ -728,8 +724,10 @@
                         int widgetId = c.getInt(indexAppWidgetId);
                         LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
                                 mContext).getLauncherAppWidgetInfo(widgetId);
-                        Point spans = pInfo == null ?
-                                mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
+                        Point spans = null;
+                        if (pInfo != null) {
+                            spans = pInfo.getMinSpans(mIdp, mContext);
+                        }
                         if (spans != null) {
                             entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
                             entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
@@ -865,7 +863,7 @@
     }
 
     private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
-        ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
+        ArrayList<DbEntry> dup = new ArrayList<>(src.size());
         for (DbEntry e : src) {
             dup.add(e.copy());
         }
@@ -909,7 +907,7 @@
         try {
             boolean dbChanged = false;
 
-            HashSet validPackages = getValidPackages(context);
+            HashSet<String> validPackages = getValidPackages(context);
             // Hotseat
             int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
             if (srcHotseatCount != idp.numHotseatIcons) {
@@ -962,7 +960,7 @@
         // this set is removed.
         // Since the loader removes such items anyway, removing these items here doesn't cause
         // any extra data loss and gives us more free space on the grid for better migration.
-        HashSet validPackages = new HashSet<>();
+        HashSet<String> validPackages = new HashSet<>();
         for (PackageInfo info : context.getPackageManager()
                 .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
             validPackages.add(info.packageName);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 36f60b9..bc7da9b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -184,9 +184,13 @@
                 icon = LauncherIcons.createIconBitmap(
                         BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
             } catch (Exception e) {
+                Log.e(TAG, "Failed to load icon for info " + info, e);
                 return null;
             }
         }
+        if (icon == null) {
+            Log.e(TAG, "Failed to load icon for info " + info);
+        }
         return icon;
     }
 
@@ -219,7 +223,7 @@
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
             }
-        } else if  (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+        } else if  (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
             if (TextUtils.isEmpty(info.title)) {
                 info.title = getTitle();
             }
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
new file mode 100644
index 0000000..b7a6b68
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -0,0 +1,386 @@
+/*
+ * 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;
+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 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);
+        }
+
+        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(mUiExecutor) : 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. */
+    private <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 MultiHashMap<PackageItemInfo, WidgetItem> widgets
+                = mBgDataModel.widgetsModel.getWidgetsMap();
+        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
new file mode 100644
index 0000000..4756edc
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -0,0 +1,860 @@
+/*
+ * 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.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.MutableInt;
+
+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.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.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+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.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.LauncherIcons;
+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.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Provider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+
+/**
+ * Runnable for the thread that loads the contents of the launcher:
+ *   - workspace icons
+ *   - widgets
+ *   - all apps icons
+ *   - deep shortcuts within apps
+ */
+public class LoaderTask implements Runnable {
+    private static final boolean DEBUG_LOADERS = false;
+    private static final String TAG = "LoaderTask";
+
+    private final LauncherAppState mApp;
+    private final AllAppsList mBgAllAppsList;
+    private final BgDataModel mBgDataModel;
+
+    private final LoaderResults mResults;
+
+    private final LauncherAppsCompat mLauncherApps;
+    private final UserManagerCompat mUserManager;
+    private final DeepShortcutManager mShortcutManager;
+    private final PackageInstallerCompat mPackageInstaller;
+    private final AppWidgetManagerCompat mAppWidgetManager;
+    private final IconCache mIconCache;
+
+    private boolean mStopped;
+
+    public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
+            LoaderResults results) {
+        mApp = app;
+        mBgAllAppsList = bgAllAppsList;
+        mBgDataModel = dataModel;
+        mResults = results;
+
+        mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+        mUserManager = UserManagerCompat.getInstance(mApp.getContext());
+        mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
+        mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
+        mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
+        mIconCache = mApp.getIconCache();
+    }
+
+    protected synchronized void waitForIdle() {
+        // Wait until the either we're stopped or the other threads are done.
+        // This way we don't start loading all apps until the workspace has settled
+        // down.
+        LooperIdleLock idleLock = mResults.newIdleLock(this);
+        // Just in case mFlushingWorkerThread changes but we aren't woken up,
+        // wait no longer than 1sec at a time
+        while (!mStopped && idleLock.awaitLocked(1000));
+    }
+
+    private synchronized void verifyNotStopped() throws CancellationException {
+        if (mStopped) {
+            throw new CancellationException("Loader stopped");
+        }
+    }
+
+    public void run() {
+        synchronized (this) {
+            // Skip fast if we are already stopped.
+            if (mStopped) {
+                return;
+            }
+        }
+
+        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
+            long now = 0;
+            if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+            loadWorkspace();
+
+            verifyNotStopped();
+            if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+            mResults.bindWorkspace();
+
+            // Take a break
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "step 1 completed, wait for idle");
+                now = SystemClock.uptimeMillis();
+            }
+            waitForIdle();
+            if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+            verifyNotStopped();
+
+            // second step
+            if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+            loadAllApps();
+
+            if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
+            verifyNotStopped();
+            mResults.bindAllApps();
+
+            verifyNotStopped();
+            if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
+            updateIconCache();
+
+            // Take a break
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "step 2 completed, wait for idle");
+                now = SystemClock.uptimeMillis();
+            }
+            waitForIdle();
+            if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+            verifyNotStopped();
+
+            // third step
+            if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+            loadDeepShortcuts();
+
+            verifyNotStopped();
+            if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+            mResults.bindDeepShortcuts();
+
+            // Take a break
+            if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
+            waitForIdle();
+            verifyNotStopped();
+
+            // fourth step
+            if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
+            mBgDataModel.widgetsModel.update(mApp, null);
+
+            verifyNotStopped();
+            if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+            mResults.bindWidgets();
+
+            transaction.commit();
+        } catch (CancellationException e) {
+            // Loader stopped, ignore
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "Loader cancelled", e);
+            }
+        }
+    }
+
+    public synchronized void stopLocked() {
+        mStopped = true;
+        this.notify();
+    }
+
+    private void loadWorkspace() {
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Loading Workspace");
+        }
+
+        final Context context = mApp.getContext();
+        final ContentResolver contentResolver = context.getContentResolver();
+        final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
+        final boolean isSafeMode = pmHelper.isSafeMode();
+        final boolean isSdCardReady = Utilities.isBootCompleted();
+        final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+
+        boolean clearDb = false;
+        try {
+            ImportDataTask.performImportIfPossible(context);
+        } catch (Exception e) {
+            // Migration failed. Clear workspace.
+            clearDb = true;
+        }
+
+        if (!clearDb && GridSizeMigrationTask.ENABLED &&
+                !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+            // Migration failed. Clear workspace.
+            clearDb = true;
+        }
+
+        if (clearDb) {
+            Log.d(TAG, "loadWorkspace: resetting launcher database");
+            LauncherSettings.Settings.call(contentResolver,
+                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+        }
+
+        Log.d(TAG, "loadWorkspace: loading default favorites");
+        LauncherSettings.Settings.call(contentResolver,
+                LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
+
+        synchronized (mBgDataModel) {
+            mBgDataModel.clear();
+
+            final HashMap<String, Integer> installingPkgs =
+                    mPackageInstaller.updateAndGetActiveSessionCache();
+            mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
+
+            Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+            final LoaderCursor c = new LoaderCursor(contentResolver.query(
+                    LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
+
+            HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
+
+            try {
+                final int appWidgetIdIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.APPWIDGET_ID);
+                final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.APPWIDGET_PROVIDER);
+                final int spanXIndex = c.getColumnIndexOrThrow
+                        (LauncherSettings.Favorites.SPANX);
+                final int spanYIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.SPANY);
+                final int rankIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.RANK);
+                final int optionsIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.OPTIONS);
+
+                final LongSparseArray<UserHandle> allUsers = c.allUsers;
+                final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
+                final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
+                for (UserHandle user : mUserManager.getUserProfiles()) {
+                    long serialNo = mUserManager.getSerialNumberForUser(user);
+                    allUsers.put(serialNo, user);
+                    quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+                    boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+                    // We can only query for shortcuts when the user is unlocked.
+                    if (userUnlocked) {
+                        List<ShortcutInfoCompat> pinnedShortcuts =
+                                mShortcutManager.queryForPinnedShortcuts(null, user);
+                        if (mShortcutManager.wasLastCallSuccess()) {
+                            for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+                                shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+                                        shortcut);
+                            }
+                        } else {
+                            // Shortcut manager can fail due to some race condition when the
+                            // lock state changes too frequently. For the purpose of the loading
+                            // shortcuts, consider the user is still locked.
+                            userUnlocked = false;
+                        }
+                    }
+                    unlockedUsers.put(serialNo, userUnlocked);
+                }
+
+                ShortcutInfo info;
+                LauncherAppWidgetInfo appWidgetInfo;
+                Intent intent;
+                String targetPkg;
+
+                FolderIconPreviewVerifier verifier =
+                        new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+                while (!mStopped && c.moveToNext()) {
+                    try {
+                        if (c.user == null) {
+                            // User has been deleted, remove the item.
+                            c.markDeleted("User has been deleted");
+                            continue;
+                        }
+
+                        boolean allowMissingTarget = false;
+                        switch (c.itemType) {
+                        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                        case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                            intent = c.parseIntent();
+                            if (intent == null) {
+                                c.markDeleted("Invalid or null intent");
+                                continue;
+                            }
+
+                            int disabledState = quietMode.get(c.serialNumber) ?
+                                    ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+                            ComponentName cn = intent.getComponent();
+                            targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
+
+                            if (!Process.myUserHandle().equals(c.user)) {
+                                if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                                    c.markDeleted("Legacy shortcuts are only allowed for default user");
+                                    continue;
+                                } else if (c.restoreFlag != 0) {
+                                    // Don't restore items for other profiles.
+                                    c.markDeleted("Restore from managed profile not supported");
+                                    continue;
+                                }
+                            }
+                            if (TextUtils.isEmpty(targetPkg) &&
+                                    c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                                c.markDeleted("Only legacy shortcuts can have null package");
+                                continue;
+                            }
+
+                            // If there is no target package, its an implicit intent
+                            // (legacy shortcut) which is always valid
+                            boolean validTarget = TextUtils.isEmpty(targetPkg) ||
+                                    mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user);
+
+                            if (cn != null && validTarget) {
+                                // If the apk is present and the shortcut points to a specific
+                                // component.
+
+                                // If the component is already present
+                                if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
+                                    // 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;
+                                        }
+                                    } else {
+                                        // The app is installed but the component is no
+                                        // longer available.
+                                        c.markDeleted("Invalid component removed: " + cn);
+                                        continue;
+                                    }
+                                }
+                            }
+                            // else if cn == null => can't infer much, leave it
+                            // else if !validPkg => could be restored icon or missing sd-card
+
+                            if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
+                                // Points to a valid app (superset of cn != null) but the apk
+                                // is not available.
+
+                                if (c.restoreFlag != 0) {
+                                    // Package is not yet available but might be
+                                    // installed later.
+                                    FileLog.d(TAG, "package not yet restored: " + targetPkg);
+
+                                    if (c.hasRestoreFlag(ShortcutInfo.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();
+                                    } 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;
+                                    // Add the icon on the workspace anyway.
+                                    allowMissingTarget = true;
+                                } else if (!isSdCardReady) {
+                                    // SdCard is not ready yet. Package might get available,
+                                    // once it is ready.
+                                    Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
+                                    pendingPackages.addToList(c.user, targetPkg);
+                                    // Add the icon on the workspace anyway.
+                                    allowMissingTarget = true;
+                                } else {
+                                    // Do not wait for external media load anymore.
+                                    c.markDeleted("Invalid package removed: " + targetPkg);
+                                    continue;
+                                }
+                            }
+
+                            if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
+                                validTarget = false;
+                            }
+
+                            if (validTarget) {
+                                // The shortcut points to a valid target (either no target
+                                // or something which is ready to be used)
+                                c.markRestored();
+                            }
+
+                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
+                                    !verifier.isItemInPreview(c.getInt(rankIndex));
+
+                            if (c.restoreFlag != 0) {
+                                // Already verified above that user is same as default user
+                                info = c.getRestoredItemInfo(intent);
+                            } else if (c.itemType ==
+                                    LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                                info = c.getAppShortcutInfo(
+                                        intent, allowMissingTarget, useLowResIcon);
+                            } else if (c.itemType ==
+                                    LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+
+                                ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+                                if (unlockedUsers.get(c.serialNumber)) {
+                                    ShortcutInfoCompat 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);
+                                        }
+                                    };
+                                    info.iconBitmap = LauncherIcons
+                                            .createShortcutIcon(pinnedShortcut, context,
+                                                    true /* badged */, fallbackIconProvider);
+                                    if (pmHelper.isAppSuspended(
+                                            pinnedShortcut.getPackage(), info.user)) {
+                                        info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+                                    }
+                                    intent = info.intent;
+                                } else {
+                                    // Create a shortcut info in disabled mode for now.
+                                    info = c.loadSimpleShortcut();
+                                    info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+                                }
+                            } else { // item type == ITEM_TYPE_SHORTCUT
+                                info = c.loadSimpleShortcut();
+
+                                // Shortcuts are only available on the primary profile
+                                if (!TextUtils.isEmpty(targetPkg)
+                                        && pmHelper.isAppSuspended(targetPkg, c.user)) {
+                                    disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+                                }
+
+                                // App shortcuts that used to be automatically added to Launcher
+                                // didn't always have the correct intent flags set, so do that
+                                // here
+                                if (intent.getAction() != null &&
+                                    intent.getCategories() != null &&
+                                    intent.getAction().equals(Intent.ACTION_MAIN) &&
+                                    intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+                                    intent.addFlags(
+                                        Intent.FLAG_ACTIVITY_NEW_TASK |
+                                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                                }
+                            }
+
+                            if (info != null) {
+                                c.applyCommonProperties(info);
+
+                                info.intent = intent;
+                                info.rank = c.getInt(rankIndex);
+                                info.spanX = 1;
+                                info.spanY = 1;
+                                info.isDisabled |= disabledState;
+                                if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+                                    info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
+                                }
+
+                                if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
+                                    Integer progress = installingPkgs.get(targetPkg);
+                                    if (progress != null) {
+                                        info.setInstallProgress(progress);
+                                    } else {
+                                        info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                    }
+                                }
+
+                                c.checkAndAddItem(info, mBgDataModel);
+                            } else {
+                                throw new RuntimeException("Unexpected null ShortcutInfo");
+                            }
+                            break;
+
+                        case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+                            FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
+                            c.applyCommonProperties(folderInfo);
+
+                            // Do not trim the folder label, as is was set by the user.
+                            folderInfo.title = c.getString(c.titleIndex);
+                            folderInfo.spanX = 1;
+                            folderInfo.spanY = 1;
+                            folderInfo.options = c.getInt(optionsIndex);
+
+                            // no special handling required for restored folders
+                            c.markRestored();
+
+                            c.checkAndAddItem(folderInfo, mBgDataModel);
+                            break;
+
+                        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                            if (FeatureFlags.GO_DISABLE_WIDGETS) {
+                                c.markDeleted("Only legacy shortcuts can have null package");
+                                continue;
+                            }
+                            // Follow through
+                        case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+                            // Read all Launcher-specific widget details
+                            boolean customWidget = c.itemType ==
+                                LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
+                            int appWidgetId = c.getInt(appWidgetIdIndex);
+                            String savedProvider = c.getString(appWidgetProviderIndex);
+
+                            final ComponentName component =
+                                    ComponentName.unflattenFromString(savedProvider);
+
+                            final boolean isIdValid = !c.hasRestoreFlag(
+                                    LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+                            final boolean wasProviderReady = !c.hasRestoreFlag(
+                                    LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
+
+                            if (widgetProvidersMap == null) {
+                                widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
+                            }
+                            final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+                                    new ComponentKey(
+                                            ComponentName.unflattenFromString(savedProvider),
+                                            c.user));
+
+                            final boolean isProviderReady = isValidProvider(provider);
+                            if (!isSafeMode && !customWidget &&
+                                    wasProviderReady && !isProviderReady) {
+                                c.markDeleted(
+                                        "Deleting widget that isn't installed anymore: "
+                                        + provider);
+                            } else {
+                                if (isProviderReady) {
+                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+                                            provider.provider);
+
+                                    // The provider is available. So the widget is either
+                                    // available or not available. We do not need to track
+                                    // any future restore updates.
+                                    int status = c.restoreFlag &
+                                            ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED &
+                                            ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+                                    if (!wasProviderReady) {
+                                        // If provider was not previously ready, update the
+                                        // status and UI flag.
+
+                                        // Id would be valid only if the widget restore broadcast was received.
+                                        if (isIdValid) {
+                                            status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                                        }
+                                    }
+                                    appWidgetInfo.restoreStatus = status;
+                                } else {
+                                    Log.v(TAG, "Widget restore pending id=" + c.id
+                                            + " appWidgetId=" + appWidgetId
+                                            + " status =" + c.restoreFlag);
+                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+                                            component);
+                                    appWidgetInfo.restoreStatus = c.restoreFlag;
+                                    Integer installProgress = installingPkgs.get(component.getPackageName());
+
+                                    if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
+                                        // Restore has started once.
+                                    } else if (installProgress != null) {
+                                        // App restore has started. Update the flag
+                                        appWidgetInfo.restoreStatus |=
+                                                LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+                                    } else if (!isSafeMode) {
+                                        c.markDeleted("Unrestored widget removed: " + component);
+                                        continue;
+                                    }
+
+                                    appWidgetInfo.installProgress =
+                                            installProgress == null ? 0 : installProgress;
+                                }
+                                if (appWidgetInfo.hasRestoreFlag(
+                                        LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+                                    appWidgetInfo.bindOptions = c.parseIntent();
+                                }
+
+                                c.applyCommonProperties(appWidgetInfo);
+                                appWidgetInfo.spanX = c.getInt(spanXIndex);
+                                appWidgetInfo.spanY = c.getInt(spanYIndex);
+                                appWidgetInfo.user = c.user;
+
+                                if (!c.isOnWorkspaceOrHotseat()) {
+                                    c.markDeleted("Widget found where container != " +
+                                            "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+                                    continue;
+                                }
+
+                                if (!customWidget) {
+                                    String providerName =
+                                            appWidgetInfo.providerName.flattenToString();
+                                    if (!providerName.equals(savedProvider) ||
+                                            (appWidgetInfo.restoreStatus != c.restoreFlag)) {
+                                        c.updater()
+                                                .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+                                                        providerName)
+                                                .put(LauncherSettings.Favorites.RESTORED,
+                                                        appWidgetInfo.restoreStatus)
+                                                .commit();
+                                    }
+                                }
+
+                                if (appWidgetInfo.restoreStatus !=
+                                        LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+                                    String pkg = appWidgetInfo.providerName.getPackageName();
+                                    appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
+                                    appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
+                                    mIconCache.getTitleAndIconForApp(
+                                            appWidgetInfo.pendingItemInfo, false);
+                                }
+
+                                c.checkAndAddItem(appWidgetInfo, mBgDataModel);
+                            }
+                            break;
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "Desktop items loading interrupted", e);
+                    }
+                }
+            } finally {
+                Utilities.closeSilently(c);
+            }
+
+            // Break early if we've stopped loading
+            if (mStopped) {
+                mBgDataModel.clear();
+                return;
+            }
+
+            // Remove dead items
+            if (c.commitDeleted()) {
+                // Remove any empty folder
+                ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+                        .call(contentResolver,
+                                LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+                        .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+                for (long folderId : deletedFolderIds) {
+                    mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+                    mBgDataModel.folders.remove(folderId);
+                    mBgDataModel.itemsIdMap.remove(folderId);
+                }
+
+                // Remove any ghost widgets
+                LauncherSettings.Settings.call(contentResolver,
+                        LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
+            }
+
+            // Unpin shortcuts that don't exist on the workspace.
+            HashSet<ShortcutKey> pendingShortcuts =
+                    InstallShortcutReceiver.getPendingShortcuts(context);
+            for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+                MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
+                if ((numTimesPinned == null || numTimesPinned.value == 0)
+                        && !pendingShortcuts.contains(key)) {
+                    // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+                    mShortcutManager.unpinShortcut(key);
+                }
+            }
+
+            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 numItemsInPreview = 0;
+                for (ShortcutInfo info : folder.contents) {
+                    if (info.usingLowResIcon
+                            && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                            && verifier.isItemInPreview(info.rank)) {
+                        mIconCache.getTitleAndIcon(info, false);
+                        numItemsInPreview++;
+                    }
+
+                    if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+                        break;
+                    }
+                }
+            }
+
+            c.commitRestoredItems();
+            if (!isSdCardReady && !pendingPackages.isEmpty()) {
+                context.registerReceiver(
+                        new SdCardAvailableReceiver(mApp, pendingPackages),
+                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+                        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);
+            }
+        }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
+        }
+    }
+
+    private void updateIconCache() {
+        // 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 (si.isPromise() && si.getTargetComponent() != null) {
+                        packagesToIgnore.add(si.getTargetComponent().getPackageName());
+                    }
+                } else if (info instanceof LauncherAppWidgetInfo) {
+                    LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
+                    if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+                        packagesToIgnore.add(lawi.providerName.getPackageName());
+                    }
+                }
+            }
+        }
+        mIconCache.updateDbIcons(packagesToIgnore);
+    }
+
+    private void loadAllApps() {
+        final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+        final List<UserHandle> profiles = mUserManager.getUserProfiles();
+
+        // Clear the list of apps
+        mBgAllAppsList.clear();
+        for (UserHandle user : profiles) {
+            // Query for the set of apps
+            final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "getActivityList took "
+                        + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+                Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+            }
+            // Fail if we don't have any apps
+            // TODO: Fix this. Only fail for the current user.
+            if (apps == null || apps.isEmpty()) {
+                return;
+            }
+            boolean quietMode = mUserManager.isQuietModeEnabled(user);
+            // Create the ApplicationInfos
+            for (int i = 0; i < apps.size(); i++) {
+                LauncherActivityInfo app = apps.get(i);
+                // This builds the icon bitmaps.
+                mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
+            }
+
+            ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
+        }
+
+        if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+            // get all active sessions and add them to the all apps list
+            for (PackageInstaller.SessionInfo info :
+                    mPackageInstaller.getAllVerifiedSessions()) {
+                mBgAllAppsList.addPromiseApp(mApp.getContext(),
+                        PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
+            }
+        }
+
+        mBgAllAppsList.added = new ArrayList<>();
+        if (DEBUG_LOADERS) {
+            Log.d(TAG, "All apps loaded in in "
+                    + (SystemClock.uptimeMillis() - loadTime) + "ms");
+        }
+    }
+
+    private void loadDeepShortcuts() {
+        mBgDataModel.deepShortcutMap.clear();
+        mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
+        if (mBgDataModel.hasShortcutHostPermission) {
+            for (UserHandle user : mUserManager.getUserProfiles()) {
+                if (mUserManager.isUserUnlocked(user)) {
+                    List<ShortcutInfoCompat> shortcuts =
+                            mShortcutManager.queryForAllShortcuts(user);
+                    mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+                }
+            }
+        }
+    }
+
+    public static boolean isValidProvider(AppWidgetProviderInfo provider) {
+        return (provider != null) && (provider.provider != null)
+                && (provider.provider.getPackageName() != null);
+    }
+}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4931dca..032ed78 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -55,7 +55,7 @@
     public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
         mContext = context;
         mBgDataModel = dataModel;
-        mWorkerExecutor = new LooperExecuter(LauncherModel.getWorkerLooper());
+        mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
         mHasVerticalHotseat = hasVerticalHotseat;
     }
 
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5d04325..32dfe25 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -16,23 +16,30 @@
 package com.android.launcher3.model;
 
 import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
 
 import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
 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.PromiseAppInfo;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.util.InstantAppResolver;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 
 /**
  * Handles changes due to a sessions updates for a currently installing app.
  */
-public class PackageInstallStateChangedTask extends ExtendedModelTask {
+public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
 
     private final PackageInstallInfo mInstallInfo;
 
@@ -43,20 +50,68 @@
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
+            try {
+                // For instant apps we do not get package-add. Use setting events to update
+                // any pinned icons.
+                ApplicationInfo ai = app.getContext()
+                        .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
+                if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
+                    app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Ignore
+            }
             // Ignore install success events as they are handled by Package add events.
             return;
         }
 
+        synchronized (apps) {
+            PromiseAppInfo updated = null;
+            final ArrayList<AppInfo> removed = new ArrayList<>();
+            for (int i=0; i < apps.size(); i++) {
+                final AppInfo appInfo = apps.get(i);
+                final ComponentName tgtComp = appInfo.getTargetComponent();
+                if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
+                    if (appInfo instanceof PromiseAppInfo) {
+                        final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+                        if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+                            promiseAppInfo.level = mInstallInfo.progress;
+                            updated = promiseAppInfo;
+                        } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+                            apps.removePromiseApp(appInfo);
+                            removed.add(appInfo);
+                        }
+                    }
+                }
+            }
+            if (updated != null) {
+                final PromiseAppInfo updatedPromiseApp = updated;
+                scheduleCallbackTask(new CallbackTask() {
+                    @Override
+                    public void execute(Callbacks callbacks) {
+                        callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
+                    }
+                });
+            }
+            if (!removed.isEmpty()) {
+                scheduleCallbackTask(new CallbackTask() {
+                    @Override
+                    public void execute(Callbacks callbacks) {
+                        callbacks.bindAppInfosRemoved(removed);
+                    }
+                });
+            }
+        }
+
         synchronized (dataModel) {
             final HashSet<ItemInfo> updates = new HashSet<>();
             for (ItemInfo info : dataModel.itemsIdMap) {
                 if (info instanceof ShortcutInfo) {
                     ShortcutInfo si = (ShortcutInfo) info;
                     ComponentName cn = si.getTargetComponent();
-                    if (si.isPromise() && (cn != null)
+                    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;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index f03c9c7..78ecbc6 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,10 +18,10 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
+import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
@@ -31,32 +31,32 @@
 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.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.LauncherIcons;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 
 /**
  * Handles updates due to changes in package manager (app installed/updated/removed)
  * or when a user availability changes.
  */
-public class PackageUpdatedTask extends ExtendedModelTask {
+public class PackageUpdatedTask extends BaseModelUpdateTask {
 
     private static final boolean DEBUG = false;
     private static final String TAG = "PackageUpdatedTask";
@@ -95,13 +95,17 @@
                 for (int i = 0; i < N; i++) {
                     if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
                     iconCache.updateIconsForPkg(packages[i], mUser);
+                    if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+                        appsList.removePackage(packages[i], Process.myUserHandle());
+                    }
                     appsList.addPackage(context, packages[i], mUser);
-                }
 
-                ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
-                if (heuristic != null) {
-                    heuristic.processPackageAdd(mPackages);
+                    // Automatically add homescreen icon for work profile apps for below O device.
+                    if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
+                        SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
+                    }
                 }
+                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -115,10 +119,6 @@
                 flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_REMOVE: {
-                ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
-                if (heuristic != null) {
-                    heuristic.processPackageRemoved(mPackages);
-                }
                 for (int i = 0; i < N; i++) {
                     iconCache.removeIconsForPkg(packages[i], mUser);
                 }
@@ -150,57 +150,37 @@
                 break;
         }
 
-        ArrayList<AppInfo> added = null;
-        ArrayList<AppInfo> modified = null;
-        final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
+        final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
+        addedOrModified.addAll(appsList.added);
+        appsList.added.clear();
+        addedOrModified.addAll(appsList.modified);
+        appsList.modified.clear();
 
-        if (appsList.added.size() > 0) {
-            added = new ArrayList<>(appsList.added);
-            appsList.added.clear();
-        }
-        if (appsList.modified.size() > 0) {
-            modified = new ArrayList<>(appsList.modified);
-            appsList.modified.clear();
-        }
-        if (appsList.removed.size() > 0) {
-            removedApps.addAll(appsList.removed);
-            appsList.removed.clear();
-        }
+        final ArrayList<AppInfo> removedApps = new ArrayList<>(appsList.removed);
+        appsList.removed.clear();
 
-        final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
-
-        if (added != null) {
-            final ArrayList<AppInfo> addedApps = added;
+        final ArrayMap<ComponentName, AppInfo> addedOrUpdatedApps = new ArrayMap<>();
+        if (!addedOrModified.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    callbacks.bindAppsAdded(null, null, null, addedApps);
+                    callbacks.bindAppsAddedOrUpdated(addedOrModified);
                 }
             });
-            for (AppInfo ai : added) {
+            for (AppInfo ai : addedOrModified) {
                 addedOrUpdatedApps.put(ai.componentName, ai);
             }
         }
 
-        if (modified != null) {
-            final ArrayList<AppInfo> modifiedFinal = modified;
-            for (AppInfo ai : modified) {
-                addedOrUpdatedApps.put(ai.componentName, ai);
-            }
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindAppsUpdated(modifiedFinal);
-                }
-            });
-        }
+        final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>();
 
         // Update shortcut infos
         if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
             final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
-            final ArrayList<ShortcutInfo> removedShortcuts = 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)) {
@@ -222,40 +202,44 @@
                         if (cn != null && matcher.matches(si, cn)) {
                             AppInfo appInfo = addedOrUpdatedApps.get(cn);
 
-                            if (si.isPromise() && mOp == OP_ADD) {
-                                if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+                            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
+                                removedShortcuts.put(si.id, false);
+                                if (mOp == OP_REMOVE) {
+                                    continue;
+                                }
+                            }
+
+                            if (si.isPromise() && isNewApkAvailable) {
+                                if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
                                     // Auto install icon
-                                    PackageManager pm = context.getPackageManager();
-                                    ResolveInfo matched = pm.resolveActivity(
-                                            new Intent(Intent.ACTION_MAIN)
-                                                    .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
-                                            PackageManager.MATCH_DEFAULT_ONLY);
-                                    if (matched == null) {
+                                    LauncherAppsCompat launcherApps
+                                            = LauncherAppsCompat.getInstance(context);
+                                    if (!launcherApps.isActivityEnabledForProfile(cn, mUser)) {
                                         // Try to find the best match activity.
-                                        Intent intent = pm.getLaunchIntentForPackage(
-                                                cn.getPackageName());
+                                        Intent intent = new PackageManagerHelper(context)
+                                                .getAppLaunchIntent(cn.getPackageName(), mUser);
                                         if (intent != null) {
                                             cn = intent.getComponent();
                                             appInfo = addedOrUpdatedApps.get(cn);
                                         }
 
-                                        if ((intent == null) || (appInfo == null)) {
-                                            removedShortcuts.add(si);
+                                        if (intent != null && appInfo != null) {
+                                            si.intent = intent;
+                                            si.status = ShortcutInfo.DEFAULT;
+                                            infoUpdated = true;
+                                        } else if (si.hasPromiseIconUi()) {
+                                            removedShortcuts.put(si.id, true);
                                             continue;
                                         }
-                                        si.intent = intent;
                                     }
-                                }
-
-                                si.status = ShortcutInfo.DEFAULT;
-                                infoUpdated = true;
-                                if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                                    iconCache.getTitleAndIcon(si, si.usingLowResIcon);
+                                } else {
+                                    si.status = ShortcutInfo.DEFAULT;
+                                    infoUpdated = true;
                                 }
                             }
 
-                            if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
-                                    && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                            if (isNewApkAvailable &&
+                                    si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
                                 iconCache.getTitleAndIcon(si, si.usingLowResIcon);
                                 infoUpdated = true;
                             }
@@ -273,7 +257,7 @@
                         if (infoUpdated) {
                             getModelWriter().updateItemInDatabase(si);
                         }
-                    } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
+                    } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
                         LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
                         if (mUser.equals(widgetInfo.user)
                                 && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
@@ -294,9 +278,9 @@
                 }
             }
 
-            bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
+            bindUpdatedShortcuts(updatedShortcuts, mUser);
             if (!removedShortcuts.isEmpty()) {
-                getModelWriter().deleteItemsFromDatabase(removedShortcuts);
+                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
             }
 
             if (!widgets.isEmpty()) {
@@ -333,22 +317,13 @@
         }
 
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(
-                    ItemInfoMatcher.ofPackages(removedPackages, mUser));
-            getModelWriter().deleteItemsFromDatabase(
-                    ItemInfoMatcher.ofComponents(removedComponents, mUser));
+            ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
+                    .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
+                    .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
+            deleteAndBindComponentsRemoved(removeMatch);
 
             // Remove any queued items from the install queue
             InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
-
-            // Call the components-removed callback
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindWorkspaceComponentsRemoved(
-                            removedPackages, removedComponents, mUser);
-                }
-            });
         }
 
         if (!removedApps.isEmpty()) {
@@ -371,14 +346,13 @@
                     callbacks.notifyWidgetProvidersChanged();
                 }
             });
-        } else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
-            // Load widgets for the new package.
+        } else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
+            // Load widgets for the new package. Changes due to app updates are handled through
+            // AppWidgetHost events, this is just to initialize the long-press options.
             for (int i = 0; i < N; i++) {
-                LauncherModel model = app.getModel();
-                model.refreshAndBindWidgetsAndShortcuts(
-                        model.getCallback(), false /* bindFirst */,
-                        new PackageUserKey(packages[i], mUser) /* packageUser */);
+                dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
             }
+            bindUpdatedWidgets(dataModel);
         }
     }
 }
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 278669b..3aedae6 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -19,9 +19,9 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.UserHandle;
 
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.MultiHashMap;
@@ -44,10 +44,10 @@
     private final Context mContext;
     private final MultiHashMap<UserHandle, String> mPackages;
 
-    public SdCardAvailableReceiver(LauncherModel model, Context context,
+    public SdCardAvailableReceiver(LauncherAppState app,
             MultiHashMap<UserHandle, String> packages) {
-        mModel = model;
-        mContext = context;
+        mModel = app.getModel();
+        mContext = app.getContext();
         mPackages = packages;
     }
 
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index d8a429c..c1f33a6 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -21,21 +21,23 @@
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
 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.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 /**
  * Handles changes due to shortcut manager updates (deep shortcut changes)
  */
-public class ShortcutsChangedTask extends ExtendedModelTask {
+public class ShortcutsChangedTask extends BaseModelUpdateTask {
 
     private final String mPackageName;
     private final List<ShortcutInfoCompat> mShortcuts;
@@ -57,54 +59,54 @@
         deepShortcutManager.onShortcutsChanged(mShortcuts);
 
         // Find ShortcutInfo's that have changed on the workspace.
-        final ArrayList<ShortcutInfo> removedShortcutInfos = new ArrayList<>();
-        MultiHashMap<String, ShortcutInfo> idsToWorkspaceShortcutInfos = new MultiHashMap<>();
+        HashSet<ShortcutKey> removedKeys = new HashSet<>();
+        MultiHashMap<ShortcutKey, ShortcutInfo> 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;
-                if (si.getIntent().getPackage().equals(mPackageName)
-                        && si.user.equals(mUser)) {
-                    idsToWorkspaceShortcutInfos.addToList(si.getDeepShortcutId(), si);
+                if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
+                    keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
+                    allIds.add(si.getDeepShortcutId());
                 }
             }
         }
 
         final ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
-        if (!idsToWorkspaceShortcutInfos.isEmpty()) {
+        if (!keyToShortcutInfo.isEmpty()) {
             // Update the workspace to reflect the changes to updated shortcuts residing on it.
             List<ShortcutInfoCompat> shortcuts = deepShortcutManager.queryForFullDetails(
-                    mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser);
+                    mPackageName, new ArrayList<>(allIds), mUser);
             for (ShortcutInfoCompat fullDetails : shortcuts) {
-                List<ShortcutInfo> shortcutInfos = idsToWorkspaceShortcutInfos
-                        .remove(fullDetails.getId());
+                ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
+                List<ShortcutInfo> shortcutInfos = keyToShortcutInfo.remove(key);
                 if (!fullDetails.isPinned()) {
                     // The shortcut was previously pinned but is no longer, so remove it from
                     // the workspace and our pinned shortcut counts.
                     // Note that we put this check here, after querying for full details,
                     // because there's a possible race condition between pinning and
                     // receiving this callback.
-                    removedShortcutInfos.addAll(shortcutInfos);
+                    removedKeys.add(key);
                     continue;
                 }
-                for (ShortcutInfo shortcutInfo : shortcutInfos) {
+                for (final ShortcutInfo shortcutInfo : shortcutInfos) {
                     shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
-                    shortcutInfo.iconBitmap =
-                            LauncherIcons.createShortcutIcon(fullDetails, context);
+                    shortcutInfo.iconBitmap = LauncherIcons.createShortcutIcon(fullDetails, context,
+                            shortcutInfo.iconBitmap);
                     updatedShortcutInfos.add(shortcutInfo);
                 }
             }
         }
 
-        // If there are still entries in idsToWorkspaceShortcutInfos, that means that
+        // If there are still entries in keyToShortcutInfo, that means that
         // the corresponding shortcuts weren't passed in onShortcutsChanged(). This
         // means they were cleared, so we remove and unpin them now.
-        for (String id : idsToWorkspaceShortcutInfos.keySet()) {
-            removedShortcutInfos.addAll(idsToWorkspaceShortcutInfos.get(id));
-        }
+        removedKeys.addAll(keyToShortcutInfo.keySet());
 
-        bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser);
-        if (!removedShortcutInfos.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(removedShortcutInfos);
+        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        if (!keyToShortcutInfo.isEmpty()) {
+            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
 
         if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 363f1ee..8170f9a 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,7 +21,6 @@
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.UserManagerCompat;
@@ -30,16 +29,18 @@
 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 java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
 /**
  * Task to handle changing of lock state of the user
  */
-public class UserLockStateChangedTask extends ExtendedModelTask {
+public class UserLockStateChangedTask extends BaseModelUpdateTask {
 
     private final UserHandle mUser;
 
@@ -71,31 +72,34 @@
 
         // Update the workspace to reflect the changes to updated shortcuts residing on it.
         ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
-        ArrayList<ShortcutInfo> deletedShortcutInfos = 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;
                 if (isUserUnlocked) {
-                    ShortcutInfoCompat shortcut = pinnedShortcuts.get(ShortcutKey.fromItemInfo(si));
+                    ShortcutKey key = ShortcutKey.fromItemInfo(si);
+                    ShortcutInfoCompat 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) {
-                        deletedShortcutInfos.add(si);
+                        removedKeys.add(key);
                         continue;
                     }
                     si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
                     si.updateFromDeepShortcutInfo(shortcut, context);
-                    si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context);
+                    si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
+                            si.iconBitmap);
                 } else {
                     si.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
                 }
                 updatedShortcutInfos.add(si);
             }
         }
-        bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser);
-        if (!deletedShortcutInfos.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(deletedShortcutInfos);
+        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        if (!removedKeys.isEmpty()) {
+            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
 
         // Remove shortcut id map for that user
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index e5215c7..ed900bf 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -18,7 +18,7 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
@@ -38,36 +38,26 @@
     private static final boolean DEBUG = false;
 
     /* Map of widgets and shortcuts that are tracked per package. */
-    private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList;
+    private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
 
-    private final IconCache mIconCache;
-    private final AppFilter mAppFilter;
+    private AppFilter mAppFilter;
 
-    public WidgetsModel(IconCache iconCache, AppFilter appFilter) {
-        mIconCache = iconCache;
-        mAppFilter = appFilter;
-        mWidgetsList = new MultiHashMap<>();
-    }
-
-    public MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
-        return mWidgetsList;
-    }
-
-    public boolean isEmpty() {
-        return mWidgetsList.isEmpty();
+    public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
+        return mWidgetsList.clone();
     }
 
     /**
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
      */
-    public ArrayList<WidgetItem> update(Context context, @Nullable PackageUserKey packageUser) {
+    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 = LauncherAppState.getIDP(context);
+            InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
 
             // Widgets
             AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
@@ -81,9 +71,9 @@
                     .getCustomShortcutActivityList(packageUser)) {
                 widgetsAndShortcuts.add(new WidgetItem(info));
             }
-            setWidgetsAndShortcuts(widgetsAndShortcuts, context, packageUser);
+            setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
         } catch (Exception e) {
-            if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(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
@@ -92,11 +82,12 @@
                 throw e;
             }
         }
-        return widgetsAndShortcuts;
+
+        app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
     }
 
-    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
-            Context context, @Nullable PackageUserKey packageUser) {
+    private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+            LauncherAppState app, @Nullable PackageUserKey packageUser) {
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
         }
@@ -133,7 +124,7 @@
             }
         }
 
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+        InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
         UserHandle myUser = Process.myUserHandle();
 
         // add and update.
@@ -152,6 +143,9 @@
                 }
             }
 
+            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.",
@@ -174,8 +168,9 @@
         }
 
         // Update each package entry
+        IconCache iconCache = app.getIconCache();
         for (PackageItemInfo p : tmpPackageItemInfos.values()) {
-            mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+            iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/notification/FlingAnimationUtils.java b/src/com/android/launcher3/notification/FlingAnimationUtils.java
deleted file mode 100644
index a1f7e49..0000000
--- a/src/com/android/launcher3/notification/FlingAnimationUtils.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.notification;
-
-import android.animation.Animator;
-import android.content.Context;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Utility class to calculate general fling animation when the finger is released.
- *
- * This class was copied from com.android.systemui.statusbar.
- */
-public class FlingAnimationUtils {
-
-    private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f;
-    private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f;
-    private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f;
-    private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f;
-    private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f;
-    private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
-    private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000;
-
-    private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 0.75f;
-    private final float mSpeedUpFactor;
-    private final float mY2;
-
-    private float mMinVelocityPxPerSecond;
-    private float mMaxLengthSeconds;
-    private float mHighVelocityPxPerSecond;
-    private float mLinearOutSlowInX2;
-
-    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
-    private PathInterpolator mInterpolator;
-    private float mCachedStartGradient = -1;
-    private float mCachedVelocityFactor = -1;
-
-    public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
-        this(ctx, maxLengthSeconds, 0.0f);
-    }
-
-    /**
-     * @param maxLengthSeconds the longest duration an animation can become in seconds
-     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
-     *                      the end of the animation. 0 means it's at the beginning and no
-     *                      acceleration will take place.
-     */
-    public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor) {
-        this(ctx, maxLengthSeconds, speedUpFactor, -1.0f, 1.0f);
-    }
-
-    /**
-     * @param maxLengthSeconds the longest duration an animation can become in seconds
-     * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards
-     *                      the end of the animation. 0 means it's at the beginning and no
-     *                      acceleration will take place.
-     * @param x2 the x value to take for the second point of the bezier spline. If a value below 0
-     *           is provided, the value is automatically calculated.
-     * @param y2 the y value to take for the second point of the bezier spline
-     */
-    public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor, float x2,
-            float y2) {
-        mMaxLengthSeconds = maxLengthSeconds;
-        mSpeedUpFactor = speedUpFactor;
-        if (x2 < 0) {
-            mLinearOutSlowInX2 = interpolate(LINEAR_OUT_SLOW_IN_X2,
-                    LINEAR_OUT_SLOW_IN_X2_MAX,
-                    mSpeedUpFactor);
-        } else {
-            mLinearOutSlowInX2 = x2;
-        }
-        mY2 = y2;
-
-        mMinVelocityPxPerSecond
-                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
-        mHighVelocityPxPerSecond
-                = HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
-    }
-
-    private static float interpolate(float start, float end, float amount) {
-        return start * (1.0f - amount) + end * amount;
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     */
-    public void apply(Animator animator, float currValue, float endValue, float velocity) {
-        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     */
-    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
-            float velocity) {
-        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     * @param maxDistance the maximum distance for this interaction; the maximum animation length
-     *                    gets multiplied by the ratio between the actual distance and this value
-     */
-    public void apply(Animator animator, float currValue, float endValue, float velocity,
-            float maxDistance) {
-        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
-                maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     * @param maxDistance the maximum distance for this interaction; the maximum animation length
-     *                    gets multiplied by the ratio between the actual distance and this value
-     */
-    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
-            float velocity, float maxDistance) {
-        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
-                maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
-    }
-
-    private AnimatorProperties getProperties(float currValue,
-            float endValue, float velocity, float maxDistance) {
-        float maxLengthSeconds = (float) (mMaxLengthSeconds
-                * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
-        float diff = Math.abs(endValue - currValue);
-        float velAbs = Math.abs(velocity);
-        float velocityFactor = mSpeedUpFactor == 0.0f
-                ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f);
-        float startGradient = interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT,
-                mY2 / mLinearOutSlowInX2, velocityFactor);
-        float durationSeconds = startGradient * diff / velAbs;
-        Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor);
-        if (durationSeconds <= maxLengthSeconds) {
-            mAnimatorProperties.interpolator = slowInInterpolator;
-        } else if (velAbs >= mMinVelocityPxPerSecond) {
-
-            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
-            durationSeconds = maxLengthSeconds;
-            VelocityInterpolator velocityInterpolator
-                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
-            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
-                    velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN);
-            mAnimatorProperties.interpolator = superInterpolator;
-        } else {
-
-            // Just use a normal interpolator which doesn't take the velocity into account.
-            durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN;
-        }
-        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
-        return mAnimatorProperties;
-    }
-
-    private Interpolator getInterpolator(float startGradient, float velocityFactor) {
-        if (startGradient != mCachedStartGradient
-                || velocityFactor != mCachedVelocityFactor) {
-            float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
-            mInterpolator = new PathInterpolator(speedup,
-                    speedup * startGradient,
-                    mLinearOutSlowInX2, mY2);
-            mCachedStartGradient = startGradient;
-            mCachedVelocityFactor = velocityFactor;
-        }
-        return mInterpolator;
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion for the case when the animation is making something
-     * disappear.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     * @param maxDistance the maximum distance for this interaction; the maximum animation length
-     *                    gets multiplied by the ratio between the actual distance and this value
-     */
-    public void applyDismissing(Animator animator, float currValue, float endValue,
-            float velocity, float maxDistance) {
-        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
-                maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
-    }
-
-    /**
-     * Applies the interpolator and length to the animator, such that the fling animation is
-     * consistent with the finger motion for the case when the animation is making something
-     * disappear.
-     *
-     * @param animator the animator to apply
-     * @param currValue the current value
-     * @param endValue the end value of the animator
-     * @param velocity the current velocity of the motion
-     * @param maxDistance the maximum distance for this interaction; the maximum animation length
-     *                    gets multiplied by the ratio between the actual distance and this value
-     */
-    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
-            float velocity, float maxDistance) {
-        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
-                maxDistance);
-        animator.setDuration(properties.duration);
-        animator.setInterpolator(properties.interpolator);
-    }
-
-    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
-            float velocity, float maxDistance) {
-        float maxLengthSeconds = (float) (mMaxLengthSeconds
-                * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
-        float diff = Math.abs(endValue - currValue);
-        float velAbs = Math.abs(velocity);
-        float y2 = calculateLinearOutFasterInY2(velAbs);
-
-        float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2;
-        Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2);
-        float durationSeconds = startGradient * diff / velAbs;
-        if (durationSeconds <= maxLengthSeconds) {
-            mAnimatorProperties.interpolator = mLinearOutFasterIn;
-        } else if (velAbs >= mMinVelocityPxPerSecond) {
-
-            // Cross fade between linear-out-faster-in and linear interpolator with current
-            // velocity.
-            durationSeconds = maxLengthSeconds;
-            VelocityInterpolator velocityInterpolator
-                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
-            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
-                    velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN);
-            mAnimatorProperties.interpolator = superInterpolator;
-        } else {
-
-            // Just use a normal interpolator which doesn't take the velocity into account.
-            durationSeconds = maxLengthSeconds;
-            mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN;
-        }
-        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
-        return mAnimatorProperties;
-    }
-
-    /**
-     * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the
-     * velocity. The faster the velocity, the more "linear" the interpolator gets.
-     *
-     * @param velocity the velocity of the gesture.
-     * @return the y2 control point for a cubic bezier path interpolator
-     */
-    private float calculateLinearOutFasterInY2(float velocity) {
-        float t = (velocity - mMinVelocityPxPerSecond)
-                / (mHighVelocityPxPerSecond - mMinVelocityPxPerSecond);
-        t = Math.max(0, Math.min(1, t));
-        return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX;
-    }
-
-    /**
-     * @return the minimum velocity a gesture needs to have to be considered a fling
-     */
-    public float getMinVelocityPxPerSecond() {
-        return mMinVelocityPxPerSecond;
-    }
-
-    /**
-     * An interpolator which interpolates two interpolators with an interpolator.
-     */
-    private static final class InterpolatorInterpolator implements Interpolator {
-
-        private Interpolator mInterpolator1;
-        private Interpolator mInterpolator2;
-        private Interpolator mCrossfader;
-
-        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
-                Interpolator crossfader) {
-            mInterpolator1 = interpolator1;
-            mInterpolator2 = interpolator2;
-            mCrossfader = crossfader;
-        }
-
-        @Override
-        public float getInterpolation(float input) {
-            float t = mCrossfader.getInterpolation(input);
-            return (1 - t) * mInterpolator1.getInterpolation(input)
-                    + t * mInterpolator2.getInterpolation(input);
-        }
-    }
-
-    /**
-     * An interpolator which interpolates with a fixed velocity.
-     */
-    private static final class VelocityInterpolator implements Interpolator {
-
-        private float mDurationSeconds;
-        private float mVelocity;
-        private float mDiff;
-
-        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
-            mDurationSeconds = durationSeconds;
-            mVelocity = velocity;
-            mDiff = diff;
-        }
-
-        @Override
-        public float getInterpolation(float input) {
-            float time = input * mDurationSeconds;
-            return time * mVelocity / mDiff;
-        }
-    }
-
-    private static class AnimatorProperties {
-        Interpolator interpolator;
-        long duration;
-    }
-
-}
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 051c033..ad07d37 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -200,12 +200,18 @@
             PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
                     Launcher.getLauncher(getContext()));
             if (popup != null) {
-                Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
+                final int newHeight = getResources().getDimensionPixelSize(
+                        R.dimen.notification_empty_footer_height);
+                Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight,
                         getResources().getInteger(R.integer.config_removeNotificationViewDuration));
                 collapseFooter.addListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
-                        ((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
+                        ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
+                        // Keep view around because gutter is aligned to it, but remove height to
+                        // both hide the view and keep calculations correct for last dismissal.
+                        getLayoutParams().height = newHeight;
+                        requestLayout();
                     }
                 });
                 collapseFooter.start();
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 1a93e11..6e36f4f 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -27,6 +27,7 @@
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.graphics.IconPalette;
@@ -93,6 +94,9 @@
 
     @Override
     public void onClick(View view) {
+        if (intent == null) {
+            return;
+        }
         final Launcher launcher = Launcher.getLauncher(view.getContext());
         Bundle activityOptions = ActivityOptions.makeClipRevealAnimation(
                 view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
@@ -105,7 +109,8 @@
         if (autoCancel) {
             launcher.getPopupDataProvider().cancelNotification(notificationKey);
         }
-        PopupContainerWithArrow.getOpen(launcher).close(true);
+        AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView
+                .TYPE_POPUP_CONTAINER_WITH_ARROW);
     }
 
     public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 997def2..ab94c32 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -17,11 +17,12 @@
 package com.android.launcher3.notification;
 
 import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Rect;
 import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -29,12 +30,16 @@
 import android.widget.TextView;
 
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.PillHeightRevealOutlineProvider;
+import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
 import com.android.launcher3.popup.PopupItemView;
+import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
 
 import java.util.List;
 
@@ -48,10 +53,11 @@
 
     private static final Rect sTempRect = new Rect();
 
+    private TextView mHeaderText;
     private TextView mHeaderCount;
     private NotificationMainView mMainView;
     private NotificationFooterLayout mFooter;
-    private SwipeHelper mSwipeHelper;
+    private SwipeDetector mSwipeDetector;
     private boolean mAnimatingNextIcon;
     private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT;
 
@@ -70,26 +76,57 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mHeaderCount = (TextView) findViewById(R.id.notification_count);
-        mMainView = (NotificationMainView) findViewById(R.id.main_view);
-        mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
-        mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext());
-        mSwipeHelper.setDisableHardwareLayers(true);
+        mHeaderText = findViewById(R.id.notification_text);
+        mHeaderCount = findViewById(R.id.notification_count);
+        mMainView = findViewById(R.id.main_view);
+        mFooter = findViewById(R.id.footer);
+
+        mSwipeDetector = new SwipeDetector(getContext(), mMainView, SwipeDetector.HORIZONTAL);
+        mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+        mMainView.setSwipeDetector(mSwipeDetector);
     }
 
     public NotificationMainView getMainView() {
         return mMainView;
     }
 
+    /**
+     * This method is used to calculate the height to remove when dismissing the last notification.
+     * We subtract the height of the footer in this case since the footer should be gone or in the
+     * process of being removed.
+     * @return The height of the entire notification item, minus the footer if it still exists.
+     */
     public int getHeightMinusFooter() {
-        int footerHeight = mFooter.getParent() == null ? 0 : mFooter.getHeight();
-        return getHeight() - footerHeight;
+        if (mFooter.getParent() == null) {
+            return getHeight();
+        }
+        int excessFooterHeight = mFooter.getHeight() - getResources().getDimensionPixelSize(
+                R.dimen.notification_empty_footer_height);
+        return getHeight() - excessFooterHeight;
     }
 
-    public Animator animateHeightRemoval(int heightToRemove) {
-        final int newHeight = getHeight() - heightToRemove;
-        return new PillHeightRevealOutlineProvider(mPillRect,
-                getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */);
+    public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) {
+        AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+
+        Rect startRect = new Rect(mPillRect);
+        Rect endRect = new Rect(mPillRect);
+        if (shouldRemoveFromTop) {
+            endRect.top += heightToRemove;
+        } else {
+            endRect.bottom -= heightToRemove;
+        }
+        anim.play(new RoundedRectRevealOutlineProvider(getBackgroundRadius(), getBackgroundRadius(),
+                startRect, endRect, mRoundedCorners).createRevealAnimator(this, false));
+
+        View bottomGutter = findViewById(R.id.gutter_bottom);
+        if (bottomGutter != null && bottomGutter.getVisibility() == VISIBLE) {
+            Animator translateGutter = ObjectAnimator.ofFloat(bottomGutter, TRANSLATION_Y,
+                    -heightToRemove);
+            translateGutter.addListener(new PropertyResetListener<>(TRANSLATION_Y, 0f));
+            anim.play(translateGutter);
+        }
+
+        return anim;
     }
 
     public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
@@ -98,8 +135,9 @@
             if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
                 mNotificationHeaderTextColor =
                         IconPalette.resolveContrastColor(getContext(), palette.dominantColor,
-                            getResources().getColor(R.color.popup_header_background_color));
+                                Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
             }
+            mHeaderText.setTextColor(mNotificationHeaderTextColor);
             mHeaderCount.setTextColor(mNotificationHeaderTextColor);
         }
     }
@@ -111,7 +149,8 @@
             return false;
         }
         getParent().requestDisallowInterceptTouchEvent(true);
-        return mSwipeHelper.onInterceptTouchEvent(ev);
+        mSwipeDetector.onTouchEvent(ev);
+        return mSwipeDetector.isDraggingOrSettling();
     }
 
     @Override
@@ -120,7 +159,7 @@
             // The notification hasn't been populated yet.
             return false;
         }
-        return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
+        return mSwipeDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
     }
 
     public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
@@ -163,13 +202,6 @@
     }
 
     @Override
-    public int getArrowColor(boolean isArrowAttachedToBottom) {
-        return ContextCompat.getColor(getContext(), isArrowAttachedToBottom
-                ? R.color.popup_background_color
-                : R.color.popup_header_background_color);
-    }
-
-    @Override
     public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
             LauncherLogProto.Target targetParent) {
         target.itemType = LauncherLogProto.ItemType.NOTIFICATION;
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 8dca699..9126626 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,36 +16,44 @@
 
 package com.android.launcher3.notification;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
 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.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SettingsObserver;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
 /**
  * A {@link NotificationListenerService} that sends updates to its
  * {@link NotificationsChangedListener} when notifications are posted or canceled,
  * as well and when this service first connects. An instance of NotificationListener,
  * and its methods for getting notifications, can be obtained via {@link #getInstanceIfConnected()}.
  */
+@TargetApi(Build.VERSION_CODES.O)
 public class NotificationListener extends NotificationListenerService {
 
+    public static final String TAG = "NotificationListener";
+
     private static final int MSG_NOTIFICATION_POSTED = 1;
     private static final int MSG_NOTIFICATION_REMOVED = 2;
     private static final int MSG_NOTIFICATION_FULL_REFRESH = 3;
@@ -53,13 +61,15 @@
     private static NotificationListener sNotificationListenerInstance = null;
     private static NotificationsChangedListener sNotificationsChangedListener;
     private static boolean sIsConnected;
+    private static boolean sIsCreated;
 
     private final Handler mWorkerHandler;
     private final Handler mUiHandler;
+    private final Ranking mTempRanking = new Ranking();
 
-    private Ranking mTempRanking = new Ranking();
+    private SettingsObserver mNotificationBadgingObserver;
 
-    private Handler.Callback mWorkerCallback = new Handler.Callback() {
+    private final Handler.Callback mWorkerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
             switch (message.what) {
@@ -70,9 +80,19 @@
                     mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
                     break;
                 case MSG_NOTIFICATION_FULL_REFRESH:
-                    final List<StatusBarNotification> activeNotifications = sIsConnected
-                            ? filterNotifications(getActiveNotifications())
-                            : new ArrayList<StatusBarNotification>();
+                    List<StatusBarNotification> activeNotifications;
+                    if (sIsConnected) {
+                        try {
+                            activeNotifications = filterNotifications(getActiveNotifications());
+                        } catch (SecurityException ex) {
+                            Log.e(TAG, "SecurityException: failed to fetch notifications");
+                            activeNotifications = new ArrayList<StatusBarNotification>();
+
+                        }
+                    } else {
+                        activeNotifications = new ArrayList<StatusBarNotification>();
+                    }
+
                     mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
                     break;
             }
@@ -80,7 +100,7 @@
         }
     };
 
-    private Handler.Callback mUiCallback = new Handler.Callback() {
+    private final Handler.Callback mUiCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
             switch (message.what) {
@@ -116,18 +136,43 @@
         sNotificationListenerInstance = this;
     }
 
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sIsCreated = true;
+        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
+            @Override
+            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+                if (!isNotificationBadgingEnabled) {
+                    requestUnbind();
+                }
+            }
+        };
+        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        sIsCreated = false;
+        mNotificationBadgingObserver.unregister();
+    }
+
     public static @Nullable NotificationListener getInstanceIfConnected() {
         return sIsConnected ? sNotificationListenerInstance : null;
     }
 
     public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
-        if (!FeatureFlags.BADGE_ICONS) {
-            return;
-        }
         sNotificationsChangedListener = listener;
 
-        if (sNotificationListenerInstance != null) {
-            sNotificationListenerInstance.onNotificationFullRefresh();
+        NotificationListener notificationListener = getInstanceIfConnected();
+        if (notificationListener != null) {
+            notificationListener.onNotificationFullRefresh();
+        } else if (!sIsCreated && sNotificationsChangedListener != null) {
+            // User turned off badging globally, so we unbound this service;
+            // tell the listener that there are no notifications to remove dots.
+            sNotificationsChangedListener.onNotificationFullRefresh(
+                    Collections.<StatusBarNotification>emptyList());
         }
     }
 
@@ -163,9 +208,9 @@
      * An object containing data to send to MSG_NOTIFICATION_POSTED targets.
      */
     private class NotificationPostedMsg {
-        PackageUserKey packageUserKey;
-        NotificationKeyData notificationKey;
-        boolean shouldBeFilteredOut;
+        final PackageUserKey packageUserKey;
+        final NotificationKeyData notificationKey;
+        final boolean shouldBeFilteredOut;
 
         NotificationPostedMsg(StatusBarNotification sbn) {
             packageUserKey = PackageUserKey.fromNotification(sbn);
@@ -189,7 +234,8 @@
         StatusBarNotification[] notifications = NotificationListener.this
                 .getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
                         .toArray(new String[keys.size()]));
-        return notifications == null ? Collections.EMPTY_LIST : Arrays.asList(notifications);
+        return notifications == null
+                ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
     }
 
     /**
@@ -201,7 +247,7 @@
     private List<StatusBarNotification> filterNotifications(
             StatusBarNotification[] notifications) {
         if (notifications == null) return null;
-        Set<Integer> removedNotifications = new HashSet<>();
+        Set<Integer> removedNotifications = new ArraySet<>();
         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 0d6da77..5aff28d 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -23,15 +23,17 @@
 import android.graphics.drawable.RippleDrawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Themes;
 
@@ -39,7 +41,7 @@
  * A {@link android.widget.FrameLayout} that contains a single notification,
  * e.g. icon + title + text.
  */
-public class NotificationMainView extends FrameLayout implements SwipeHelper.Callback {
+public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener {
 
     private NotificationInfo mNotificationInfo;
     private ViewGroup mTextAndBackground;
@@ -47,6 +49,8 @@
     private TextView mTitleView;
     private TextView mTextView;
 
+    private SwipeDetector mSwipeDetector;
+
     public NotificationMainView(Context context) {
         this(context, null, 0);
     }
@@ -78,6 +82,10 @@
         applyNotificationInfo(mainNotification, iconView, false);
     }
 
+    public void setSwipeDetector(SwipeDetector swipeDetector) {
+        mSwipeDetector = swipeDetector;
+    }
+
     /**
      * Sets the content of this view, animating it after a new icon shifts up if necessary.
      */
@@ -87,11 +95,11 @@
         CharSequence title = mNotificationInfo.title;
         CharSequence text = mNotificationInfo.text;
         if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(text)) {
-            mTitleView.setText(title);
-            mTextView.setText(text);
+            mTitleView.setText(title.toString());
+            mTextView.setText(text.toString());
         } else {
             mTitleView.setMaxLines(2);
-            mTitleView.setText(TextUtils.isEmpty(title) ? text : title);
+            mTitleView.setText(TextUtils.isEmpty(title) ? text.toString() : title.toString());
             mTextView.setVisibility(GONE);
         }
         iconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
@@ -113,29 +121,11 @@
     }
 
 
-    // SwipeHelper.Callback's
-
-    @Override
-    public View getChildAtPosition(MotionEvent ev) {
-        return this;
-    }
-
-    @Override
-    public boolean canChildBeDismissed(View v) {
+    public boolean canChildBeDismissed() {
         return mNotificationInfo != null && mNotificationInfo.dismissable;
     }
 
-    @Override
-    public boolean isAntiFalsingNeeded() {
-        return false;
-    }
-
-    @Override
-    public void onBeginDrag(View v) {
-    }
-
-    @Override
-    public void onChildDismissed(View v) {
+    public void onChildDismissed() {
         Launcher launcher = Launcher.getLauncher(getContext());
         launcher.getPopupDataProvider().cancelNotification(
                 mNotificationInfo.notificationKey);
@@ -145,22 +135,55 @@
                 LauncherLogProto.ItemType.NOTIFICATION);
     }
 
+    // SwipeDetector.Listener's
     @Override
-    public void onDragCancelled(View v) {
-    }
+    public void onDragStart(boolean start) { }
+
 
     @Override
-    public void onChildSnappedBack(View animView, float targetLeft) {
-    }
-
-    @Override
-    public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
-        // Don't fade out.
+    public boolean onDrag(float displacement, float velocity) {
+        setTranslationX(canChildBeDismissed()
+                ? displacement : OverScroll.dampedScroll(displacement, getWidth()));
+        animate().cancel();
         return true;
     }
 
     @Override
-    public float getFalsingThresholdFactor() {
-        return 1;
+    public void onDragEnd(float velocity, boolean fling) {
+        final boolean willExit;
+        final float endTranslation;
+
+        if (!canChildBeDismissed()) {
+            willExit = false;
+            endTranslation = 0;
+        } else if (fling) {
+            willExit = true;
+            endTranslation = velocity < 0 ? - getWidth() : getWidth();
+        } else if (Math.abs(getTranslationX()) > getWidth() / 2) {
+            willExit = true;
+            endTranslation = (getTranslationX() < 0 ? -getWidth() : getWidth());
+        } else {
+            willExit = false;
+            endTranslation = 0;
+        }
+
+        SwipeDetector.ScrollInterpolator interpolator = new SwipeDetector.ScrollInterpolator();
+        interpolator.setVelocityAtZero(velocity);
+
+        long duration = SwipeDetector.calculateDuration(velocity,
+                (endTranslation - getTranslationX()) / getWidth());
+        animate()
+                .setDuration(duration)
+                .setInterpolator(interpolator)
+                .translationX(endTranslation)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mSwipeDetector.finishedScrolling();
+                        if (willExit) {
+                            onChildDismissed();
+                        }
+                    }
+                }).start();
     }
 }
diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java
deleted file mode 100644
index 5f03252..0000000
--- a/src/com/android/launcher3/notification/SwipeHelper.java
+++ /dev/null
@@ -1,690 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.notification;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.RectF;
-import android.os.Handler;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-
-import com.android.launcher3.R;
-
-import java.util.HashMap;
-
-/**
- * This class was copied from com.android.systemui.
- */
-public class SwipeHelper {
-    static final String TAG = "SwipeHelper";
-    private static final boolean DEBUG = false;
-    private static final boolean DEBUG_INVALIDATE = false;
-    private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
-    private static final boolean CONSTRAIN_SWIPE = true;
-    private static final boolean FADE_OUT_DURING_SWIPE = true;
-    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
-    public static final int X = 0;
-    public static final int Y = 1;
-
-    private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
-    private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
-    private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
-    private int MAX_DISMISS_VELOCITY = 4000; // dp/sec
-    private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
-
-    static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
-                                                // beyond which swipe progress->0
-    private float mMinSwipeProgress = 0f;
-    private float mMaxSwipeProgress = 1f;
-
-    private FlingAnimationUtils mFlingAnimationUtils;
-    private float mPagingTouchSlop;
-    private Callback mCallback;
-    private Handler mHandler;
-    private int mSwipeDirection;
-    private VelocityTracker mVelocityTracker;
-
-    private float mInitialTouchPos;
-    private float mPerpendicularInitialTouchPos;
-    private boolean mDragging;
-    private boolean mSnappingChild;
-    private View mCurrView;
-    private boolean mCanCurrViewBeDimissed;
-    private float mDensityScale;
-    private float mTranslation = 0;
-
-    private boolean mLongPressSent;
-    private LongPressListener mLongPressListener;
-    private Runnable mWatchLongPress;
-    private long mLongPressTimeout;
-
-    final private int[] mTmpPos = new int[2];
-    private int mFalsingThreshold;
-    private boolean mTouchAboveFalsingThreshold;
-    private boolean mDisableHwLayers;
-
-    private HashMap<View, Animator> mDismissPendingMap = new HashMap<>();
-
-    public SwipeHelper(int swipeDirection, Callback callback, Context context) {
-        mCallback = callback;
-        mHandler = new Handler();
-        mSwipeDirection = swipeDirection;
-        mVelocityTracker = VelocityTracker.obtain();
-        mDensityScale =  context.getResources().getDisplayMetrics().density;
-        mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
-
-        mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press!
-        mFalsingThreshold = context.getResources().getDimensionPixelSize(
-                R.dimen.swipe_helper_falsing_threshold);
-        mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
-    }
-
-    public void setLongPressListener(LongPressListener listener) {
-        mLongPressListener = listener;
-    }
-
-    public void setDensityScale(float densityScale) {
-        mDensityScale = densityScale;
-    }
-
-    public void setPagingTouchSlop(float pagingTouchSlop) {
-        mPagingTouchSlop = pagingTouchSlop;
-    }
-
-    public void setDisableHardwareLayers(boolean disableHwLayers) {
-        mDisableHwLayers = disableHwLayers;
-    }
-
-    private float getPos(MotionEvent ev) {
-        return mSwipeDirection == X ? ev.getX() : ev.getY();
-    }
-
-    private float getPerpendicularPos(MotionEvent ev) {
-        return mSwipeDirection == X ? ev.getY() : ev.getX();
-    }
-
-    protected float getTranslation(View v) {
-        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
-    }
-
-    private float getVelocity(VelocityTracker vt) {
-        return mSwipeDirection == X ? vt.getXVelocity() :
-                vt.getYVelocity();
-    }
-
-    protected ObjectAnimator createTranslationAnimation(View v, float newPos) {
-        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
-                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
-        return anim;
-    }
-
-    private float getPerpendicularVelocity(VelocityTracker vt) {
-        return mSwipeDirection == X ? vt.getYVelocity() :
-                vt.getXVelocity();
-    }
-
-    protected Animator getViewTranslationAnimator(View v, float target,
-            AnimatorUpdateListener listener) {
-        ObjectAnimator anim = createTranslationAnimation(v, target);
-        if (listener != null) {
-            anim.addUpdateListener(listener);
-        }
-        return anim;
-    }
-
-    protected void setTranslation(View v, float translate) {
-        if (v == null) {
-            return;
-        }
-        if (mSwipeDirection == X) {
-            v.setTranslationX(translate);
-        } else {
-            v.setTranslationY(translate);
-        }
-    }
-
-    protected float getSize(View v) {
-        return mSwipeDirection == X ? v.getMeasuredWidth() :
-                v.getMeasuredHeight();
-    }
-
-    public void setMinSwipeProgress(float minSwipeProgress) {
-        mMinSwipeProgress = minSwipeProgress;
-    }
-
-    public void setMaxSwipeProgress(float maxSwipeProgress) {
-        mMaxSwipeProgress = maxSwipeProgress;
-    }
-
-    private float getSwipeProgressForOffset(View view, float translation) {
-        float viewSize = getSize(view);
-        float result = Math.abs(translation / viewSize);
-        return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress);
-    }
-
-    private float getSwipeAlpha(float progress) {
-        return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END));
-    }
-
-    private void updateSwipeProgressFromOffset(View animView, boolean dismissable) {
-        updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView));
-    }
-
-    private void updateSwipeProgressFromOffset(View animView, boolean dismissable,
-            float translation) {
-        float swipeProgress = getSwipeProgressForOffset(animView, translation);
-        if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
-            if (FADE_OUT_DURING_SWIPE && dismissable) {
-                float alpha = swipeProgress;
-                if (!mDisableHwLayers) {
-                    if (alpha != 0f && alpha != 1f) {
-                        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                    } else {
-                        animView.setLayerType(View.LAYER_TYPE_NONE, null);
-                    }
-                }
-                animView.setAlpha(getSwipeAlpha(swipeProgress));
-            }
-        }
-        invalidateGlobalRegion(animView);
-    }
-
-    // invalidate the view's own bounds all the way up the view hierarchy
-    public static void invalidateGlobalRegion(View view) {
-        invalidateGlobalRegion(
-                view,
-                new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
-    }
-
-    // invalidate a rectangle relative to the view's coordinate system all the way up the view
-    // hierarchy
-    public static void invalidateGlobalRegion(View view, RectF childBounds) {
-        //childBounds.offset(view.getTranslationX(), view.getTranslationY());
-        if (DEBUG_INVALIDATE)
-            Log.v(TAG, "-------------");
-        while (view.getParent() != null && view.getParent() instanceof View) {
-            view = (View) view.getParent();
-            view.getMatrix().mapRect(childBounds);
-            view.invalidate((int) Math.floor(childBounds.left),
-                    (int) Math.floor(childBounds.top),
-                    (int) Math.ceil(childBounds.right),
-                    (int) Math.ceil(childBounds.bottom));
-            if (DEBUG_INVALIDATE) {
-                Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
-                        + "," + (int) Math.floor(childBounds.top)
-                        + "," + (int) Math.ceil(childBounds.right)
-                        + "," + (int) Math.ceil(childBounds.bottom));
-            }
-        }
-    }
-
-    public void removeLongPressCallback() {
-        if (mWatchLongPress != null) {
-            mHandler.removeCallbacks(mWatchLongPress);
-            mWatchLongPress = null;
-        }
-    }
-
-    public boolean onInterceptTouchEvent(final MotionEvent ev) {
-        final int action = ev.getAction();
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mTouchAboveFalsingThreshold = false;
-                mDragging = false;
-                mSnappingChild = false;
-                mLongPressSent = false;
-                mVelocityTracker.clear();
-                mCurrView = mCallback.getChildAtPosition(ev);
-
-                if (mCurrView != null) {
-                    onDownUpdate(mCurrView);
-                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
-                    mVelocityTracker.addMovement(ev);
-                    mInitialTouchPos = getPos(ev);
-                    mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
-                    mTranslation = getTranslation(mCurrView);
-                    if (mLongPressListener != null) {
-                        if (mWatchLongPress == null) {
-                            mWatchLongPress = new Runnable() {
-                                @Override
-                                public void run() {
-                                    if (mCurrView != null && !mLongPressSent) {
-                                        mLongPressSent = true;
-                                        mCurrView.sendAccessibilityEvent(
-                                                AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
-                                        mCurrView.getLocationOnScreen(mTmpPos);
-                                        final int x = (int) ev.getRawX() - mTmpPos[0];
-                                        final int y = (int) ev.getRawY() - mTmpPos[1];
-                                        mLongPressListener.onLongPress(mCurrView, x, y);
-                                    }
-                                }
-                            };
-                        }
-                        mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
-                    }
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null && !mLongPressSent) {
-                    mVelocityTracker.addMovement(ev);
-                    float pos = getPos(ev);
-                    float perpendicularPos = getPerpendicularPos(ev);
-                    float delta = pos - mInitialTouchPos;
-                    float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos;
-                    if (Math.abs(delta) > mPagingTouchSlop
-                            && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
-                        mCallback.onBeginDrag(mCurrView);
-                        mDragging = true;
-                        mInitialTouchPos = getPos(ev);
-                        mTranslation = getTranslation(mCurrView);
-                        removeLongPressCallback();
-                    }
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                final boolean captured = (mDragging || mLongPressSent);
-                mDragging = false;
-                mCurrView = null;
-                mLongPressSent = false;
-                removeLongPressCallback();
-                if (captured) return true;
-                break;
-        }
-        return mDragging || mLongPressSent;
-    }
-
-    /**
-     * @param view The view to be dismissed
-     * @param velocity The desired pixels/second speed at which the view should move
-     * @param useAccelerateInterpolator Should an accelerating Interpolator be used
-     */
-    public void dismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
-        dismissChild(view, velocity, null /* endAction */, 0 /* delay */,
-                useAccelerateInterpolator, 0 /* fixedDuration */, false /* isDismissAll */);
-    }
-
-    /**
-     * @param animView The view to be dismissed
-     * @param velocity The desired pixels/second speed at which the view should move
-     * @param endAction The action to perform at the end
-     * @param delay The delay after which we should start
-     * @param useAccelerateInterpolator Should an accelerating Interpolator be used
-     * @param fixedDuration If not 0, this exact duration will be taken
-     */
-    public void dismissChild(final View animView, float velocity, final Runnable endAction,
-            long delay, boolean useAccelerateInterpolator, long fixedDuration,
-            boolean isDismissAll) {
-        final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
-        float newPos;
-        boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-
-        // if we use the Menu to dismiss an item in landscape, animate up
-        boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
-                && mSwipeDirection == Y;
-        // if the language is rtl we prefer swiping to the left
-        boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
-                && isLayoutRtl;
-        boolean animateLeft = velocity < 0
-                || (velocity == 0 && getTranslation(animView) < 0 && !isDismissAll);
-
-        if (animateLeft || animateLeftForRtl || animateUpForMenu) {
-            newPos = -getSize(animView);
-        } else {
-            newPos = getSize(animView);
-        }
-        long duration;
-        if (fixedDuration == 0) {
-            duration = MAX_ESCAPE_ANIMATION_DURATION;
-            if (velocity != 0) {
-                duration = Math.min(duration,
-                        (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math
-                                .abs(velocity))
-                );
-            } else {
-                duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
-            }
-        } else {
-            duration = fixedDuration;
-        }
-
-        if (!mDisableHwLayers) {
-            animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        }
-        AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            public void onAnimationUpdate(ValueAnimator animation) {
-                onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
-            }
-        };
-
-        Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
-        if (anim == null) {
-            return;
-        }
-        if (useAccelerateInterpolator) {
-            anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-            anim.setDuration(duration);
-        } else {
-            mFlingAnimationUtils.applyDismissing(anim, getTranslation(animView),
-                    newPos, velocity, getSize(animView));
-        }
-        if (delay > 0) {
-            anim.setStartDelay(delay);
-        }
-        anim.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
-            }
-
-            public void onAnimationEnd(Animator animation) {
-                updateSwipeProgressFromOffset(animView, canBeDismissed);
-                mDismissPendingMap.remove(animView);
-                if (!mCancelled) {
-                    mCallback.onChildDismissed(animView);
-                }
-                if (endAction != null) {
-                    endAction.run();
-                }
-                if (!mDisableHwLayers) {
-                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
-            }
-        });
-
-        prepareDismissAnimation(animView, anim);
-        mDismissPendingMap.put(animView, anim);
-        anim.start();
-    }
-
-    /**
-     * Called to update the dismiss animation.
-     */
-    protected void prepareDismissAnimation(View view, Animator anim) {
-        // Do nothing
-    }
-
-    public void snapChild(final View animView, final float targetLeft, float velocity) {
-        final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
-        AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
-            public void onAnimationUpdate(ValueAnimator animation) {
-                onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed);
-            }
-        };
-
-        Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
-        if (anim == null) {
-            return;
-        }
-        int duration = SNAP_ANIM_LEN;
-        anim.setDuration(duration);
-        anim.addListener(new AnimatorListenerAdapter() {
-            public void onAnimationEnd(Animator animator) {
-                mSnappingChild = false;
-                updateSwipeProgressFromOffset(animView, canBeDismissed);
-                mCallback.onChildSnappedBack(animView, targetLeft);
-            }
-        });
-        prepareSnapBackAnimation(animView, anim);
-        mSnappingChild = true;
-        anim.start();
-    }
-
-    /**
-     * Called to update the snap back animation.
-     */
-    protected void prepareSnapBackAnimation(View view, Animator anim) {
-        // Do nothing
-    }
-
-    /**
-     * Called when there's a down event.
-     */
-    public void onDownUpdate(View currView) {
-        // Do nothing
-    }
-
-    /**
-     * Called on a move event.
-     */
-    protected void onMoveUpdate(View view, float totalTranslation, float delta) {
-        // Do nothing
-    }
-
-    /**
-     * Called in {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} when the current
-     * view is being animated to dismiss or snap.
-     */
-    public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
-        updateSwipeProgressFromOffset(animView, canBeDismissed, value);
-    }
-
-    private void snapChildInstantly(final View view) {
-        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
-        setTranslation(view, 0);
-        updateSwipeProgressFromOffset(view, canAnimViewBeDismissed);
-    }
-
-    /**
-     * Called when a view is updated to be non-dismissable, if the view was being dismissed before
-     * the update this will handle snapping it back into place.
-     *
-     * @param view the view to snap if necessary.
-     * @param animate whether to animate the snap or not.
-     * @param targetLeft the target to snap to.
-     */
-    public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) {
-        if ((mDragging && mCurrView == view) || mSnappingChild) {
-            return;
-        }
-        boolean needToSnap = false;
-        Animator dismissPendingAnim = mDismissPendingMap.get(view);
-        if (dismissPendingAnim != null) {
-            needToSnap = true;
-            dismissPendingAnim.cancel();
-        } else if (getTranslation(view) != 0) {
-            needToSnap = true;
-        }
-        if (needToSnap) {
-            if (animate) {
-                snapChild(view, targetLeft, 0.0f /* velocity */);
-            } else {
-                snapChildInstantly(view);
-            }
-        }
-    }
-
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mLongPressSent) {
-            return true;
-        }
-
-        if (!mDragging) {
-            if (mCallback.getChildAtPosition(ev) != null) {
-
-                // We are dragging directly over a card, make sure that we also catch the gesture
-                // even if nobody else wants the touch event.
-                onInterceptTouchEvent(ev);
-                 return true;
-            } else {
-
-                // We are not doing anything, make sure the long press callback
-                // is not still ticking like a bomb waiting to go off.
-                removeLongPressCallback();
-                return false;
-            }
-        }
-
-        mVelocityTracker.addMovement(ev);
-        final int action = ev.getAction();
-        switch (action) {
-            case MotionEvent.ACTION_OUTSIDE:
-            case MotionEvent.ACTION_MOVE:
-                if (mCurrView != null) {
-                    float delta = getPos(ev) - mInitialTouchPos;
-                    float absDelta = Math.abs(delta);
-                    if (absDelta >= getFalsingThreshold()) {
-                        mTouchAboveFalsingThreshold = true;
-                    }
-                    // don't let items that can't be dismissed be dragged more than
-                    // maxScrollDistance
-                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
-                        float size = getSize(mCurrView);
-                        float maxScrollDistance = 0.25f * size;
-                        if (absDelta >= size) {
-                            delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
-                        } else {
-                            delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
-                        }
-                    }
-
-                    setTranslation(mCurrView, mTranslation + delta);
-                    updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed);
-                    onMoveUpdate(mCurrView, mTranslation + delta, delta);
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                if (mCurrView == null) {
-                    break;
-                }
-                mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
-                float velocity = getVelocity(mVelocityTracker);
-
-                if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
-                    if (isDismissGesture(ev)) {
-                        // flingadingy
-                        dismissChild(mCurrView, velocity,
-                                !swipedFastEnough() /* useAccelerateInterpolator */);
-                    } else {
-                        // snappity
-                        mCallback.onDragCancelled(mCurrView);
-                        snapChild(mCurrView, 0 /* leftTarget */, velocity);
-                    }
-                    mCurrView = null;
-                }
-                mDragging = false;
-                break;
-        }
-        return true;
-    }
-
-    private int getFalsingThreshold() {
-        float factor = mCallback.getFalsingThresholdFactor();
-        return (int) (mFalsingThreshold * factor);
-    }
-
-    private float getMaxVelocity() {
-        return MAX_DISMISS_VELOCITY * mDensityScale;
-    }
-
-    protected float getEscapeVelocity() {
-        return getUnscaledEscapeVelocity() * mDensityScale;
-    }
-
-    protected float getUnscaledEscapeVelocity() {
-        return SWIPE_ESCAPE_VELOCITY;
-    }
-
-    protected long getMaxEscapeAnimDuration() {
-        return MAX_ESCAPE_ANIMATION_DURATION;
-    }
-
-    protected boolean swipedFarEnough() {
-        float translation = getTranslation(mCurrView);
-        return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
-    }
-
-    protected boolean isDismissGesture(MotionEvent ev) {
-        boolean falsingDetected = mCallback.isAntiFalsingNeeded() && !mTouchAboveFalsingThreshold;
-        return !falsingDetected && (swipedFastEnough() || swipedFarEnough())
-                && ev.getActionMasked() == MotionEvent.ACTION_UP
-                && mCallback.canChildBeDismissed(mCurrView);
-    }
-
-    protected boolean swipedFastEnough() {
-        float velocity = getVelocity(mVelocityTracker);
-        float translation = getTranslation(mCurrView);
-        boolean ret = (Math.abs(velocity) > getEscapeVelocity())
-                && (velocity > 0) == (translation > 0);
-        return ret;
-    }
-
-    protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
-            float translation) {
-        return false;
-    }
-
-    public interface Callback {
-        View getChildAtPosition(MotionEvent ev);
-
-        boolean canChildBeDismissed(View v);
-
-        boolean isAntiFalsingNeeded();
-
-        void onBeginDrag(View v);
-
-        void onChildDismissed(View v);
-
-        void onDragCancelled(View v);
-
-        /**
-         * Called when the child is snapped to a position.
-         *
-         * @param animView the view that was snapped.
-         * @param targetLeft the left position the view was snapped to.
-         */
-        void onChildSnappedBack(View animView, float targetLeft);
-
-        /**
-         * Updates the swipe progress on a child.
-         *
-         * @return if true, prevents the default alpha fading.
-         */
-        boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress);
-
-        /**
-         * @return The factor the falsing threshold should be multiplied with
-         */
-        float getFalsingThresholdFactor();
-    }
-
-    /**
-     * Equivalent to View.OnLongClickListener with coordinates
-     */
-    public interface LongPressListener {
-        /**
-         * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
-         * @return whether the longpress was handled
-         */
-        boolean onLongPress(View v, int x, int y);
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
index 0a00e24..5ade497 100644
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -22,12 +22,11 @@
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
 
 import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
 
-import android.graphics.drawable.Drawable;
-
 public class CaretDrawable extends Drawable {
     public static final float PROGRESS_CARET_POINTING_UP = -1f;
     public static final float PROGRESS_CARET_POINTING_DOWN = 1f;
@@ -39,6 +38,7 @@
     private Paint mCaretPaint = new Paint();
     private Path mPath = new Path();
     private final int mCaretSizePx;
+    private final boolean mUseShadow;
 
     public CaretDrawable(Context context) {
         final Resources res = context.getResources();
@@ -46,12 +46,12 @@
         final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
         final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
 
-        mCaretPaint.setColor(res.getColor(R.color.workspace_icon_text_color));
+        mCaretPaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
         mCaretPaint.setAntiAlias(true);
         mCaretPaint.setStrokeWidth(strokeWidth);
         mCaretPaint.setStyle(Paint.Style.STROKE);
-        mCaretPaint.setStrokeCap(Paint.Cap.SQUARE);
-        mCaretPaint.setStrokeJoin(Paint.Join.MITER);
+        mCaretPaint.setStrokeCap(Paint.Cap.ROUND);
+        mCaretPaint.setStrokeJoin(Paint.Join.ROUND);
 
         mShadowPaint.setColor(res.getColor(R.color.default_shadow_color_no_alpha));
         mShadowPaint.setAlpha(Themes.getAlpha(context, android.R.attr.spotShadowAlpha));
@@ -61,6 +61,7 @@
         mShadowPaint.setStrokeCap(Paint.Cap.ROUND);
         mShadowPaint.setStrokeJoin(Paint.Join.ROUND);
 
+        mUseShadow = !Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText);
         mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size);
     }
 
@@ -95,8 +96,9 @@
         mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
         mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
         mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
-
-        canvas.drawPath(mPath, mShadowPaint);
+        if (mUseShadow) {
+            canvas.drawPath(mPath, mShadowPaint);
+        }
         canvas.drawPath(mPath, mCaretPaint);
     }
 
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
index aedf283..911be93 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
@@ -16,9 +16,7 @@
 package com.android.launcher3.pageindicators;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 
@@ -50,9 +48,7 @@
         setCaretDrawable(caretDrawable);
 
         Launcher l = Launcher.getLauncher(context);
-        setOnTouchListener(l.getHapticFeedbackTouchListener());
         setOnClickListener(l);
-        setOnLongClickListener(l);
         setOnFocusChangeListener(l.mFocusHandler);
     }
 
@@ -60,7 +56,7 @@
     protected void onDraw(Canvas canvas) {
         Rect drawableBounds = getCaretDrawable().getBounds();
         int count = canvas.save();
-        canvas.translate(getWidth() - drawableBounds.width(),
+        canvas.translate((getWidth() - drawableBounds.width()) / 2,
                 getHeight() - drawableBounds.height());
         getCaretDrawable().draw(canvas);
         canvas.restoreToCount(count);
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 3ceba84..6281fec 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -21,7 +21,9 @@
 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.dynamicui.ExtractedColors;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
 
 /**
  * A PageIndicator that briefly shows a fraction of a line when moving between pages.
@@ -128,6 +130,10 @@
         mLauncher = Launcher.getLauncher(context);
         mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
         setCaretDrawable(new CaretDrawable(context));
+
+        boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
+        mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
+        mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
     }
 
     @Override
@@ -135,9 +141,7 @@
         super.onFinishInflate();
         mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
         mAllAppsHandle.setImageDrawable(getCaretDrawable());
-        mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
         mAllAppsHandle.setOnClickListener(mLauncher);
-        mAllAppsHandle.setOnLongClickListener(mLauncher);
         mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
         mLauncher.setAllAppsButton(mAllAppsHandle);
     }
@@ -220,8 +224,11 @@
      * - mostly opaque black if the hotseat is black (ignoring alpha)
      */
     public void updateColor(ExtractedColors extractedColors) {
+        if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+            return;
+        }
         int originalLineAlpha = mLinePaint.getAlpha();
-        int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+        int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
         if (color != Color.TRANSPARENT) {
             color = ColorUtils.setAlphaComponent(color, 255);
             if (color == Color.BLACK) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 4488f66..8441598 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -26,13 +26,16 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.CornerPathEffect;
+import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
+import android.support.annotation.IntDef;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -40,8 +43,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
+import android.view.animation.AccelerateDecelerateInterpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
@@ -51,14 +53,13 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LogAccelerateInterpolator;
 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.anim.PropertyListBuilder;
 import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -71,12 +72,16 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutsItemView;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -88,6 +93,16 @@
 public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
         DragController.DragListener {
 
+    public static final int ROUNDED_TOP_CORNERS = 1 << 0;
+    public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
+
+    @IntDef(flag = true, value = {
+            ROUNDED_TOP_CORNERS,
+            ROUNDED_BOTTOM_CORNERS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public  @interface RoundedCornerFlags {}
+
     protected final Launcher mLauncher;
     private final int mStartDragThreshold;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
@@ -102,10 +117,13 @@
     private boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
     private View mArrow;
+    private int mGravity;
 
     protected Animator mOpenCloseAnimator;
     private boolean mDeferContainerRemoval;
     private AnimatorSet mReduceHeightAnimatorSet;
+    private final Rect mStartRect = new Rect();
+    private final Rect mEndRect = new Rect();
 
     public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -174,7 +192,7 @@
         // Add dummy views first, and populate with real info when ready.
         PopupPopulator.Item[] itemsToPopulate = PopupPopulator
                 .getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
-        addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
+        addDummyViews(itemsToPopulate, notificationKeys.size());
 
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -185,7 +203,7 @@
             mNotificationItemView = null;
             mShortcutsItemView = null;
             itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
-            addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
+            addDummyViews(itemsToPopulate, notificationKeys.size());
 
             measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
             orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -221,6 +239,7 @@
         mArrow.setPivotX(arrowWidth / 2);
         mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
 
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         animateOpen();
 
         mLauncher.getDragController().addDragListener(this);
@@ -234,49 +253,87 @@
                 systemShortcuts, systemShortcutViews));
     }
 
-    private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate,
-            boolean notificationFooterHasIcons) {
+    private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) {
         final Resources res = getResources();
-        final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing);
         final LayoutInflater inflater = mLauncher.getLayoutInflater();
 
+        int shortcutsItemRoundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
         int numItems = itemTypesToPopulate.length;
         for (int i = 0; i < numItems; i++) {
             PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+            PopupPopulator.Item prevItemTypeToPopulate =
+                    i > 0 ? itemTypesToPopulate[i - 1] : null;
             PopupPopulator.Item nextItemTypeToPopulate =
                     i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
             final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
 
+            boolean shouldUnroundTopCorners = prevItemTypeToPopulate != null
+                    && itemTypeToPopulate.isShortcut ^ prevItemTypeToPopulate.isShortcut;
+            boolean shouldUnroundBottomCorners = nextItemTypeToPopulate != null
+                    && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
+
             if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
                 mNotificationItemView = (NotificationItemView) item;
-                int footerHeight = notificationFooterHasIcons ?
-                        res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
+                boolean notificationFooterHasIcons = numNotifications > 1;
+                int footerHeight = res.getDimensionPixelSize(
+                        notificationFooterHasIcons ? R.dimen.notification_footer_height
+                                : R.dimen.notification_empty_footer_height);
                 item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
+                if (notificationFooterHasIcons) {
+                    mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
+                }
+
+                int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+                if (shouldUnroundTopCorners) {
+                    roundedCorners &= ~ROUNDED_TOP_CORNERS;
+                    mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE);
+                }
+                if (shouldUnroundBottomCorners) {
+                    roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+                    mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE);
+                }
+                int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
+                mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
+
                 mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate);
             } else if (itemTypeToPopulate == PopupPopulator.Item.SHORTCUT) {
                 item.setAccessibilityDelegate(mAccessibilityDelegate);
             }
 
-            boolean shouldAddBottomMargin = nextItemTypeToPopulate != null
-                    && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
-
             if (itemTypeToPopulate.isShortcut) {
                 if (mShortcutsItemView == null) {
                     mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
                             R.layout.shortcuts_item, this, false);
                     addView(mShortcutsItemView);
+                    if (shouldUnroundTopCorners) {
+                        shortcutsItemRoundedCorners &= ~ROUNDED_TOP_CORNERS;
+                    }
+                }
+                if (itemTypeToPopulate != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON
+                        && numNotifications > 0) {
+                    int prevHeight = item.getLayoutParams().height;
+                    // Condense shortcuts height when there are notifications.
+                    item.getLayoutParams().height = res.getDimensionPixelSize(
+                            R.dimen.bg_popup_item_condensed_height);
+                    if (item instanceof DeepShortcutView) {
+                        float iconScale = (float) item.getLayoutParams().height / prevHeight;
+                        ((DeepShortcutView) item).getIconView().setScaleX(iconScale);
+                        ((DeepShortcutView) item).getIconView().setScaleY(iconScale);
+                    }
                 }
                 mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
-                if (shouldAddBottomMargin) {
-                    ((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing;
+                if (shouldUnroundBottomCorners) {
+                    shortcutsItemRoundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
                 }
             } else {
                 addView(item);
-                if (shouldAddBottomMargin) {
-                    ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
-                }
             }
         }
+        int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+        mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
+        if (numNotifications > 0) {
+            mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
+        }
     }
 
     protected PopupItemView getItemViewAt(int index) {
@@ -296,45 +353,38 @@
         setVisibility(View.VISIBLE);
         mIsOpen = true;
 
-        final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
-        final int itemCount = getItemCount();
+        final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+        final Resources res = getResources();
+        final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
 
-        final long duration = getResources().getInteger(
-                R.integer.config_deepShortcutOpenDuration);
-        final long arrowScaleDuration = getResources().getInteger(
-                R.integer.config_deepShortcutArrowOpenDuration);
-        final long arrowScaleDelay = duration - arrowScaleDuration;
-        final long stagger = getResources().getInteger(
-                R.integer.config_deepShortcutOpenStagger);
-        final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
-
-        // Animate shortcuts
-        DecelerateInterpolator interpolator = new DecelerateInterpolator();
-        for (int i = 0; i < itemCount; i++) {
-            final PopupItemView popupItemView = getItemViewAt(i);
-            popupItemView.setVisibility(INVISIBLE);
-            popupItemView.setAlpha(0);
-
-            Animator anim = popupItemView.createOpenAnimation(mIsAboveIcon, mIsLeftAligned);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    popupItemView.setVisibility(VISIBLE);
-                }
-            });
-            anim.setDuration(duration);
-            int animationIndex = mIsAboveIcon ? itemCount - i - 1 : i;
-            anim.setStartDelay(stagger * animationIndex);
-            anim.setInterpolator(interpolator);
-            shortcutAnims.play(anim);
-
-            Animator fadeAnim = ObjectAnimator.ofFloat(popupItemView, View.ALPHA, 1);
-            fadeAnim.setInterpolator(fadeInterpolator);
-            // We want the shortcut to be fully opaque before the arrow starts animating.
-            fadeAnim.setDuration(arrowScaleDelay);
-            shortcutAnims.play(fadeAnim);
+        // Rectangular reveal.
+        int itemsTotalHeight = 0;
+        for (int i = 0; i < getItemCount(); i++) {
+            itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
         }
-        shortcutAnims.addListener(new AnimatorListenerAdapter() {
+        Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+        int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+        float radius = getItemViewAt(0).getBackgroundRadius();
+        mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+        mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+        final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
+                (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
+        revealAnim.setDuration(revealDuration);
+        revealAnim.setInterpolator(revealInterpolator);
+
+        Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
+        fadeIn.setDuration(revealDuration);
+        fadeIn.setInterpolator(revealInterpolator);
+        openAnim.play(fadeIn);
+
+        // Animate the arrow.
+        mArrow.setScaleX(0);
+        mArrow.setScaleY(0);
+        Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
+                R.integer.config_popupArrowOpenDuration));
+
+        openAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mOpenCloseAnimator = null;
@@ -345,15 +395,49 @@
             }
         });
 
-        // Animate the arrow
-        mArrow.setScaleX(0);
-        mArrow.setScaleY(0);
-        Animator arrowScale = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
-        arrowScale.setStartDelay(arrowScaleDelay);
-        shortcutAnims.play(arrowScale);
+        mOpenCloseAnimator = openAnim;
+        openAnim.playSequentially(revealAnim, arrowScale);
+        openAnim.start();
+    }
 
-        mOpenCloseAnimator = shortcutAnims;
-        shortcutAnims.start();
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        enforceContainedWithinScreen(l, r);
+
+    }
+
+    private void enforceContainedWithinScreen(int left, int right) {
+        DragLayer dragLayer = mLauncher.getDragLayer();
+        if (getTranslationX() + left < 0 ||
+                getTranslationX() + right > dragLayer.getWidth()) {
+            // If we are still off screen, center horizontally too.
+            mGravity |= Gravity.CENTER_HORIZONTAL;
+        }
+
+        if (Gravity.isHorizontal(mGravity)) {
+            setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
+        }
+        if (Gravity.isVertical(mGravity)) {
+            setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
+        }
+    }
+
+    /**
+     * Returns the point at which the center of the arrow merges with the first popup item.
+     */
+    private Point computeAnimStartPoint(int itemsTotalHeight) {
+        int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+                R.dimen.popup_arrow_horizontal_center_start:
+                R.dimen.popup_arrow_horizontal_center_end);
+        if (!mIsLeftAligned) {
+            arrowCenterX = getMeasuredWidth() - arrowCenterX;
+        }
+        int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
+                - itemsTotalHeight;
+        // The y-coordinate of edge between the arrow and the first popup item.
+        int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
+        return new Point(arrowCenterX, arrowEdge);
     }
 
     /**
@@ -413,7 +497,9 @@
         x += mIsLeftAligned ? xOffset : -xOffset;
 
         // Open above icon if there is room.
-        int iconHeight = icon.getIcon().getBounds().height();
+        int iconHeight = icon.getIcon() != null
+                ? icon.getIcon().getBounds().height()
+                : icon.getHeight();
         int y = mTempRect.top + icon.getPaddingTop() - height;
         mIsAboveIcon = y > dragLayer.getTop() + insets.top;
         if (!mIsAboveIcon) {
@@ -428,9 +514,10 @@
         }
         y -= insets.top;
 
-        if (y < dragLayer.getTop() || y + height > dragLayer.getBottom()) {
+        mGravity = 0;
+        if (y + height > dragLayer.getBottom() - insets.bottom) {
             // The container is opening off the screen, so just center it in the drag layer instead.
-            ((FrameLayout.LayoutParams) getLayoutParams()).gravity = Gravity.CENTER_VERTICAL;
+            mGravity = Gravity.CENTER_VERTICAL;
             // Put the container next to the icon, preferring the right side in ltr (left in rtl).
             int rightSide = leftAlignedX + iconWidth - insets.left;
             int leftSide = rightAlignedX - iconWidth - insets.left;
@@ -454,18 +541,8 @@
             mIsAboveIcon = true;
         }
 
-        if (x < dragLayer.getLeft() || x + width > dragLayer.getRight()) {
-            // If we are still off screen, center horizontally too.
-            ((FrameLayout.LayoutParams) getLayoutParams()).gravity |= Gravity.CENTER_HORIZONTAL;
-        }
-
-        int gravity = ((FrameLayout.LayoutParams) getLayoutParams()).gravity;
-        if (!Gravity.isHorizontal(gravity)) {
-            setX(x);
-        }
-        if (!Gravity.isVertical(gravity)) {
-            setY(y);
-        }
+        setX(x);
+        setY(y);
     }
 
     private boolean isAlignedWithStart() {
@@ -493,7 +570,7 @@
         }
 
         View arrowView = new View(getContext());
-        if (Gravity.isVertical(((FrameLayout.LayoutParams) getLayoutParams()).gravity)) {
+        if (Gravity.isVertical(mGravity)) {
             // This is only true if there wasn't room for the container next to the icon,
             // so we centered it instead. In that case we don't want to show the arrow.
             arrowView.setVisibility(INVISIBLE);
@@ -505,7 +582,7 @@
             // since the latter expects the arrow which hasn't been added yet.
             PopupItemView itemAttachedToArrow = (PopupItemView)
                     (getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0));
-            arrowPaint.setColor(itemAttachedToArrow.getArrowColor(mIsAboveIcon));
+            arrowPaint.setColor(Themes.getAttrColor(mLauncher, 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));
@@ -529,6 +606,7 @@
      */
     public DragOptions.PreDragCondition createPreDragCondition() {
         return new DragOptions.PreDragCondition() {
+
             @Override
             public boolean shouldStartDrag(double distanceDragged) {
                 return distanceDragged > mStartDragThreshold;
@@ -536,15 +614,27 @@
 
             @Override
             public void onPreDragStart(DropTarget.DragObject dragObject) {
-                mOriginalIcon.setVisibility(INVISIBLE);
+                if (mIsAboveIcon) {
+                    // Hide only the icon, keep the text visible.
+                    mOriginalIcon.setIconVisible(false);
+                    mOriginalIcon.setVisibility(VISIBLE);
+                } else {
+                    // Hide both the icon and text.
+                    mOriginalIcon.setVisibility(INVISIBLE);
+                }
             }
 
             @Override
             public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
-                if (!dragStarted) {
-                    mOriginalIcon.setVisibility(VISIBLE);
+                mOriginalIcon.setIconVisible(true);
+                if (dragStarted) {
+                    // Make sure we keep the original icon hidden while it is being dragged.
+                    mOriginalIcon.setVisibility(INVISIBLE);
+                } else {
                     mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
                     if (!mIsAboveIcon) {
+                        // Show the icon but keep the text hidden.
+                        mOriginalIcon.setVisibility(VISIBLE);
                         mOriginalIcon.setTextVisibility(false);
                     }
                 }
@@ -590,25 +680,22 @@
         ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
         BadgeInfo badgeInfo = updatedBadges.get(PackageUserKey.fromItemInfo(originalInfo));
         if (badgeInfo == null || badgeInfo.getNotificationKeys().size() == 0) {
+            // There are no more notifications, so create an animation to remove
+            // the notifications view and expand the shortcuts view (if possible).
             AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
+            int hiddenShortcutsHeight = 0;
+            if (mShortcutsItemView != null) {
+                hiddenShortcutsHeight = mShortcutsItemView.getHiddenShortcutsHeight();
+                int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+                // With notifications gone, all corners of shortcuts item should be rounded.
+                mShortcutsItemView.setBackgroundWithCorners(backgroundColor,
+                        ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
+                removeNotification.play(mShortcutsItemView.showAllShortcuts(mIsAboveIcon));
+            }
             final int duration = getResources().getInteger(
                     R.integer.config_removeNotificationViewDuration);
-            final int spacing = getResources().getDimensionPixelSize(R.dimen.popup_items_spacing);
-            removeNotification.play(reduceNotificationViewHeight(
-                    mNotificationItemView.getHeightMinusFooter() + spacing, duration));
-            final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2)
-                    : mNotificationItemView;
-            if (removeMarginView != null) {
-                ValueAnimator removeMargin = ValueAnimator.ofFloat(1, 0).setDuration(duration);
-                removeMargin.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                        ((MarginLayoutParams) removeMarginView.getLayoutParams()).bottomMargin
-                                = (int) (spacing * (float) valueAnimator.getAnimatedValue());
-                    }
-                });
-                removeNotification.play(removeMargin);
-            }
+            removeNotification.play(adjustItemHeights(mNotificationItemView.getHeightMinusFooter(),
+                    hiddenShortcutsHeight, duration));
             Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0)
                     .setDuration(duration);
             fade.addListener(new AnimatorListenerAdapter() {
@@ -618,13 +705,12 @@
                     mNotificationItemView = null;
                     if (getItemCount() == 0) {
                         close(false);
-                        return;
                     }
                 }
             });
             removeNotification.play(fade);
             final long arrowScaleDuration = getResources().getInteger(
-                    R.integer.config_deepShortcutArrowOpenDuration);
+                    R.integer.config_popupArrowOpenDuration);
             Animator hideArrow = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
             hideArrow.setStartDelay(0);
             Animator showArrow = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
@@ -649,28 +735,50 @@
                 mArrow, new PropertyListBuilder().scale(scale).build());
     }
 
+    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+        return adjustItemHeights(heightToRemove, 0, duration);
+    }
+
     /**
      * Animates the height of the notification item and the translationY of other items accordingly.
      */
-    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+    public Animator adjustItemHeights(int notificationHeightToRemove, int shortcutHeightToAdd,
+            int duration) {
         if (mReduceHeightAnimatorSet != null) {
             mReduceHeightAnimatorSet.cancel();
         }
-        final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
+        final int translateYBy = mIsAboveIcon ? notificationHeightToRemove - shortcutHeightToAdd
+                : -notificationHeightToRemove;
         mReduceHeightAnimatorSet = LauncherAnimUtils.createAnimatorSet();
-        mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(heightToRemove));
+        boolean removingNotification =
+                notificationHeightToRemove == mNotificationItemView.getHeightMinusFooter();
+        boolean shouldRemoveNotificationHeightFromTop = mIsAboveIcon && removingNotification;
+        mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(
+                notificationHeightToRemove, shouldRemoveNotificationHeightFromTop));
         PropertyResetListener<View, Float> resetTranslationYListener
                 = new PropertyResetListener<>(TRANSLATION_Y, 0f);
+        boolean itemIsAfterShortcuts = false;
         for (int i = 0; i < getItemCount(); i++) {
             final PopupItemView itemView = getItemViewAt(i);
-            if (!mIsAboveIcon && itemView == mNotificationItemView) {
-                // The notification view is already in the right place when container is below icon.
+            if (itemIsAfterShortcuts) {
+                // Every item after the shortcuts item needs to adjust for the new height.
+                itemView.setTranslationY(itemView.getTranslationY() - shortcutHeightToAdd);
+            }
+            if (itemView == mNotificationItemView && (!mIsAboveIcon || removingNotification)) {
+                // The notification view is already in the right place.
                 continue;
             }
             ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
                     itemView.getTranslationY() + translateYBy).setDuration(duration);
             translateItem.addListener(resetTranslationYListener);
             mReduceHeightAnimatorSet.play(translateItem);
+            if (itemView == mShortcutsItemView) {
+                itemIsAfterShortcuts = true;
+            }
+        }
+        if (mIsAboveIcon) {
+            // We also need to adjust the arrow position to account for the new shortcuts height.
+            mArrow.setTranslationY(mArrow.getTranslationY() - shortcutHeightToAdd);
         }
         mReduceHeightAnimatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -680,6 +788,7 @@
                     // container itself did not. This means the items would jump back to their
                     // original translation unless we update the container's translationY here.
                     setTranslationY(getTranslationY() + translateYBy);
+                    mArrow.setTranslationY(0);
                 }
                 mReduceHeightAnimatorSet = null;
             }
@@ -754,55 +863,49 @@
         if (!mIsOpen) {
             return;
         }
+        mEndRect.setEmpty();
         if (mOpenCloseAnimator != null) {
+            Outline outline = new Outline();
+            getOutlineProvider().getOutline(this, outline);
+            outline.getRect(mEndRect);
             mOpenCloseAnimator.cancel();
         }
         mIsOpen = false;
 
-        final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
-        final int itemCount = getItemCount();
-        int numOpenShortcuts = 0;
-        for (int i = 0; i < itemCount; i++) {
-            if (getItemViewAt(i).isOpenOrOpening()) {
-                numOpenShortcuts++;
-            }
+        final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+        final Resources res = getResources();
+        final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+        // Rectangular reveal (reversed).
+        int itemsTotalHeight = 0;
+        for (int i = 0; i < getItemCount(); i++) {
+            itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
         }
-        final long duration = getResources().getInteger(
-                R.integer.config_deepShortcutCloseDuration);
-        final long arrowScaleDuration = getResources().getInteger(
-                R.integer.config_deepShortcutArrowOpenDuration);
-        final long stagger = getResources().getInteger(
-                R.integer.config_deepShortcutCloseStagger);
-        final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
-
-        int firstOpenItemIndex = mIsAboveIcon ? itemCount - numOpenShortcuts : 0;
-        for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) {
-            final PopupItemView view = getItemViewAt(i);
-            Animator anim;
-            anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
-            int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
-                    : numOpenShortcuts - i - 1;
-            anim.setStartDelay(stagger * animationIndex);
-
-            Animator fadeAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0);
-            // Don't start fading until the arrow is gone.
-            fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration);
-            fadeAnim.setDuration(duration - arrowScaleDuration);
-            fadeAnim.setInterpolator(fadeInterpolator);
-            shortcutAnims.play(fadeAnim);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    view.setVisibility(INVISIBLE);
-                }
-            });
-            shortcutAnims.play(anim);
+        Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+        int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+        float radius = getItemViewAt(0).getBackgroundRadius();
+        mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+        if (mEndRect.isEmpty()) {
+            mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
         }
-        Animator arrowAnim = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
-        arrowAnim.setStartDelay(0);
-        shortcutAnims.play(arrowAnim);
+        final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
+                radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
+        revealAnim.setDuration(revealDuration);
+        revealAnim.setInterpolator(revealInterpolator);
+        closeAnim.play(revealAnim);
 
-        shortcutAnims.addListener(new AnimatorListenerAdapter() {
+        Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+        fadeOut.setDuration(revealDuration);
+        fadeOut.setInterpolator(revealInterpolator);
+        closeAnim.play(fadeOut);
+
+        // Animate original icon's text back in.
+        Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */);
+        fadeText.setDuration(revealDuration);
+        closeAnim.play(fadeText);
+
+        closeAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mOpenCloseAnimator = null;
@@ -813,8 +916,8 @@
                 }
             }
         });
-        mOpenCloseAnimator = shortcutAnims;
-        shortcutAnims.start();
+        mOpenCloseAnimator = closeAnim;
+        closeAnim.start();
         mOriginalIcon.forceHideBadge(false);
     }
 
@@ -828,9 +931,7 @@
         }
         mIsOpen = false;
         mDeferContainerRemoval = false;
-        boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
-                == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-        mOriginalIcon.setTextVisibility(!isInHotseat);
+        mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
         mOriginalIcon.forceHideBadge(false);
         mLauncher.getDragController().removeDragListener(this);
         mLauncher.getDragLayer().removeView(this);
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 384f554..8ec051b 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -16,38 +16,34 @@
 
 package com.android.launcher3.popup;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.LogAccelerateInterpolator;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags;
+
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;
 
 /**
- * An abstract {@link FrameLayout} that supports animating an item's content
- * (e.g. icon and text) separate from the item's background.
+ * An abstract {@link FrameLayout} that contains content for {@link PopupContainerWithArrow}.
  */
-public abstract class PopupItemView extends FrameLayout
-        implements ValueAnimator.AnimatorUpdateListener {
-
-    protected static final Point sTempPoint = new Point();
+public abstract class PopupItemView extends FrameLayout {
 
     protected final Rect mPillRect;
-    private float mOpenAnimationProgress;
+    protected  @RoundedCornerFlags int mRoundedCorners;
     protected final boolean mIsRtl;
     protected View mIconView;
 
@@ -93,164 +89,57 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        if (mRoundedCorners == 0) {
+            super.dispatchDraw(canvas);
+            return;
+        }
+
         int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
         super.dispatchDraw(canvas);
 
+        // Clip children to this item's rounded corners.
         int cornerWidth = mRoundedCornerBitmap.getWidth();
         int cornerHeight = mRoundedCornerBitmap.getHeight();
-        // Clip top left corner.
-        mMatrix.reset();
-        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
-        // Clip top right corner.
-        mMatrix.setRotate(90, cornerWidth / 2, cornerHeight / 2);
-        mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0);
-        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
-        // Clip bottom right corner.
-        mMatrix.setRotate(180, cornerWidth / 2, cornerHeight / 2);
-        mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight);
-        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
-        // Clip bottom left corner.
-        mMatrix.setRotate(270, cornerWidth / 2, cornerHeight / 2);
-        mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight);
-        canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        int cornerCenterX = Math.round(cornerWidth / 2f);
+        int cornerCenterY = Math.round(cornerHeight / 2f);
+        if ((mRoundedCorners & ROUNDED_TOP_CORNERS) != 0) {
+            // Clip top left corner.
+            mMatrix.reset();
+            canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+            // Clip top right corner.
+            mMatrix.setRotate(90, cornerCenterX, cornerCenterY);
+            mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0);
+            canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        }
+        if ((mRoundedCorners & ROUNDED_BOTTOM_CORNERS) != 0) {
+            // Clip bottom right corner.
+            mMatrix.setRotate(180, cornerCenterX, cornerCenterY);
+            mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight);
+            canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+            // Clip bottom left corner.
+            mMatrix.setRotate(270, cornerCenterX, cornerCenterY);
+            mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight);
+            canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+        }
 
         canvas.restoreToCount(saveCount);
     }
 
     /**
-     * Creates an animator to play when the shortcut container is being opened.
+     * Creates a round rect drawable (with the specified corners unrounded)
+     * and sets it as this View's background.
      */
-    public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
-        Point center = getIconCenter();
-        int arrowCenter = getResources().getDimensionPixelSize(pivotLeft ^ mIsRtl ?
-                R.dimen.popup_arrow_horizontal_center_start:
-                R.dimen.popup_arrow_horizontal_center_end);
-        ValueAnimator openAnimator =  new ZoomRevealOutlineProvider(center.x, center.y,
-                mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft, arrowCenter)
-                        .createRevealAnimator(this, false);
-        mOpenAnimationProgress = 0f;
-        openAnimator.addUpdateListener(this);
-        return openAnimator;
-    }
-
-    @Override
-    public void onAnimationUpdate(ValueAnimator valueAnimator) {
-        mOpenAnimationProgress = valueAnimator.getAnimatedFraction();
-    }
-
-    public boolean isOpenOrOpening() {
-        return mOpenAnimationProgress > 0;
-    }
-
-    /**
-     * Creates an animator to play when the shortcut container is being closed.
-     */
-    public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
-            long duration) {
-        Point center = getIconCenter();
-        int arrowCenter = getResources().getDimensionPixelSize(pivotLeft ^ mIsRtl ?
-                R.dimen.popup_arrow_horizontal_center_start :
-                R.dimen.popup_arrow_horizontal_center_end);
-        ValueAnimator closeAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
-                mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft, arrowCenter)
-                        .createRevealAnimator(this, true);
-        // Scale down the duration and interpolator according to the progress
-        // that the open animation was at when the close started.
-        closeAnimator.setDuration((long) (duration * mOpenAnimationProgress));
-        closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress));
-        closeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mOpenAnimationProgress = 0;
-            }
-        });
-        return closeAnimator;
-    }
-
-    /**
-     * Returns the position of the center of the icon relative to the container.
-     */
-    public Point getIconCenter() {
-        sTempPoint.y = getMeasuredHeight() / 2;
-        sTempPoint.x = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height) / 2;
-        if (Utilities.isRtl(getResources())) {
-            sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
-        }
-        return sTempPoint;
+    public void setBackgroundWithCorners(int color, @RoundedCornerFlags int roundedCorners) {
+        mRoundedCorners = roundedCorners;
+        float rTop = (roundedCorners & ROUNDED_TOP_CORNERS) == 0 ? 0 : getBackgroundRadius();
+        float rBot = (roundedCorners & ROUNDED_BOTTOM_CORNERS) == 0 ? 0 : getBackgroundRadius();
+        float[] radii = new float[] {rTop, rTop, rTop, rTop, rBot, rBot, rBot, rBot};
+        ShapeDrawable roundRectBackground = new ShapeDrawable(new RoundRectShape(radii, null, null));
+        roundRectBackground.getPaint().setColor(color);
+        setBackground(roundRectBackground);
     }
 
     protected float getBackgroundRadius() {
         return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
     }
-
-    public abstract int getArrowColor(boolean isArrowAttachedToBottom);
-
-    /**
-     * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
-     */
-    private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
-
-        private final View mTranslateView;
-        private final View mZoomView;
-
-        private final float mFullHeight;
-        private final float mTranslateYMultiplier;
-
-        private final boolean mPivotLeft;
-        private final float mTranslateX;
-        private final float mArrowCenter;
-
-        public ZoomRevealOutlineProvider(int x, int y, Rect pillRect, PopupItemView translateView,
-                View zoomView, boolean isContainerAboveIcon, boolean pivotLeft, float arrowCenter) {
-            super(x, y, pillRect, translateView.getBackgroundRadius());
-            mTranslateView = translateView;
-            mZoomView = zoomView;
-            mFullHeight = pillRect.height();
-
-            mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
-
-            mPivotLeft = pivotLeft;
-            mTranslateX = pivotLeft ? arrowCenter : pillRect.right - arrowCenter;
-            mArrowCenter = arrowCenter;
-        }
-
-        @Override
-        public void setProgress(float progress) {
-            super.setProgress(progress);
-
-            if (mZoomView != null) {
-                mZoomView.setScaleX(progress);
-                mZoomView.setScaleY(progress);
-            }
-
-            float height = mOutline.height();
-            mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
-
-            float offsetX = Math.min(mOutline.width(), mArrowCenter);
-            float pivotX = mPivotLeft ? (mOutline.left + offsetX) : (mOutline.right - offsetX);
-            mTranslateView.setTranslationX(mTranslateX - pivotX);
-        }
-    }
-
-    /**
-     * An interpolator that reverses the current open animation progress.
-     */
-    private static class CloseInterpolator extends LogAccelerateInterpolator {
-        private float mStartProgress;
-        private float mRemainingProgress;
-
-        /**
-         * @param openAnimationProgress The progress that the open interpolator ended at.
-         */
-        public CloseInterpolator(float openAnimationProgress) {
-            super(100, 0);
-            mStartProgress = 1f - openAnimationProgress;
-            mRemainingProgress = openAnimationProgress;
-        }
-
-        @Override
-        public float getInterpolation(float v) {
-            return mStartProgress + super.getInterpolation(v) * mRemainingProgress;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index c62d877..0dc1ca0 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -52,9 +52,9 @@
  */
 public class PopupPopulator {
 
-    public static final int MAX_ITEMS = 4;
+    public static final int MAX_SHORTCUTS = 4;
     @VisibleForTesting static final int NUM_DYNAMIC = 2;
-    private static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
+    public static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
 
     public enum Item {
         SHORTCUT(R.layout.deep_shortcut, true),
@@ -77,10 +77,7 @@
         boolean hasNotifications = notificationKeys.size() > 0;
         int numNotificationItems = hasNotifications ? 1 : 0;
         int numShortcuts = shortcutIds.size();
-        if (hasNotifications && numShortcuts > MAX_SHORTCUTS_IF_NOTIFICATIONS) {
-            numShortcuts = MAX_SHORTCUTS_IF_NOTIFICATIONS;
-        }
-        int numItems = Math.min(MAX_ITEMS, numShortcuts + numNotificationItems)
+        int numItems = Math.min(MAX_SHORTCUTS, numShortcuts) + numNotificationItems
                 + systemShortcuts.size();
         Item[] items = new Item[numItems];
         for (int i = 0; i < numItems; i++) {
@@ -126,12 +123,12 @@
     };
 
     /**
-     * Filters the shortcuts so that only MAX_ITEMS or fewer shortcuts are retained.
+     * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
      * We want the filter to include both static and dynamic shortcuts, so we always
      * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
      *
      * @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
-     * @return a subset of shortcuts, in sorted order, with size <= MAX_ITEMS.
+     * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
      */
     public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
             List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
@@ -147,27 +144,27 @@
         }
 
         Collections.sort(shortcuts, SHORTCUT_RANK_COMPARATOR);
-        if (shortcuts.size() <= MAX_ITEMS) {
+        if (shortcuts.size() <= MAX_SHORTCUTS) {
             return shortcuts;
         }
 
         // The list of shortcuts is now sorted with static shortcuts followed by dynamic
-        // shortcuts. We want to preserve this order, but only keep MAX_ITEMS.
-        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_ITEMS);
+        // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
+        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
         int numDynamic = 0;
         int size = shortcuts.size();
         for (int i = 0; i < size; i++) {
             ShortcutInfoCompat shortcut = shortcuts.get(i);
             int filteredSize = filteredShortcuts.size();
-            if (filteredSize < MAX_ITEMS) {
-                // Always add the first MAX_ITEMS to the filtered list.
+            if (filteredSize < MAX_SHORTCUTS) {
+                // Always add the first MAX_SHORTCUTS to the filtered list.
                 filteredShortcuts.add(shortcut);
                 if (shortcut.isDynamic()) {
                     numDynamic++;
                 }
                 continue;
             }
-            // At this point, we have MAX_ITEMS already, but they may all be static.
+            // At this point, we have MAX_SHORTCUTS already, but they may all be static.
             // If there are dynamic shortcuts, remove static shortcuts to add them.
             if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
                 numDynamic++;
@@ -305,14 +302,12 @@
         if (view instanceof DeepShortcutView) {
             // Expanded system shortcut, with both icon and text shown on white background.
             final DeepShortcutView shortcutView = (DeepShortcutView) view;
-            shortcutView.getIconView().setBackground(info.getIcon(context,
-                    android.R.attr.textColorTertiary));
+            shortcutView.getIconView().setBackground(info.getIcon(context));
             shortcutView.getBubbleText().setText(info.getLabel(context));
         } else if (view instanceof ImageView) {
             // Only the system shortcut icon shows on a gray background header.
             final ImageView shortcutIcon = (ImageView) view;
-            shortcutIcon.setImageDrawable(info.getIcon(context,
-                    android.R.attr.textColorHint));
+            shortcutIcon.setImageDrawable(info.getIcon(context));
             shortcutIcon.setContentDescription(info.getLabel(context));
         }
         view.setTag(info);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index f158f71..6254d2d 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -13,18 +13,20 @@
 import com.android.launcher3.R;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.widget.WidgetsBottomSheet;
 
 import java.util.List;
 
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
 /**
  * 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.
  *
  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
  */
-public abstract class SystemShortcut {
+public abstract class SystemShortcut extends ItemInfo {
     private final int mIconResId;
     private final int mLabelResId;
 
@@ -33,10 +35,8 @@
         mLabelResId = labelResId;
     }
 
-    public Drawable getIcon(Context context, int colorAttr) {
-        Drawable icon = context.getResources().getDrawable(mIconResId, context.getTheme()).mutate();
-        icon.setTint(Themes.getAttrColor(context, colorAttr));
-        return icon;
+    public Drawable getIcon(Context context) {
+        return context.getResources().getDrawable(mIconResId, context.getTheme());
     }
 
     public String getLabel(Context context) {
@@ -68,6 +68,8 @@
                             (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
                                     R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
                     widgetsBottomSheet.populateAndShow(itemInfo);
+                    launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+                            ControlType.WIDGETS_BUTTON, view);
                 }
             };
         }
@@ -87,6 +89,8 @@
                     Rect sourceBounds = launcher.getViewBounds(view);
                     Bundle opts = launcher.getActivityLaunchOptions(view);
                     InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts);
+                    launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+                            ControlType.APPINFO_TARGET, view);
                 }
             };
         }
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b0482f8..b83d3c0 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.provider;
 
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
 import android.content.ContentProviderOperation;
 import android.content.ContentValues;
 import android.content.Context;
@@ -29,14 +31,14 @@
 import android.net.Uri;
 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;
 import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
@@ -46,14 +48,11 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.util.LongArrayMap;
-
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 
 /**
@@ -112,7 +111,7 @@
             screenOps.add(ContentProviderOperation.newInsert(
                     LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
         }
-        mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY, screenOps);
+        mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, screenOps);
         importWorkspaceItems(allScreens.get(0), screenIdMap);
 
         GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
@@ -133,7 +132,7 @@
         String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
                 .getSerialNumberForUser(Process.myUserHandle()));
 
-        boolean createEmptyRowOnFirstScreen = false;
+        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
@@ -289,7 +288,7 @@
                 }
 
                 if (insertOperations.size() >= BATCH_INSERT_SIZE) {
-                    mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                    mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                             insertOperations);
                     insertOperations.clear();
                 }
@@ -300,7 +299,7 @@
             throw new Exception("Insufficient data");
         }
         if (!insertOperations.isEmpty()) {
-            mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                     insertOperations);
             insertOperations.clear();
         }
@@ -319,15 +318,15 @@
             mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
 
             if (!insertOperations.isEmpty()) {
-                mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                         insertOperations);
             }
         }
     }
 
-    private static final String getPackage(Intent intent) {
+    private static String getPackage(Intent intent) {
         return intent.getComponent() != null ? intent.getComponent().getPackageName()
-                : intent.getPackage();
+            : intent.getPackage();
     }
 
     /**
@@ -377,11 +376,7 @@
         return false;
     }
 
-    private static SharedPreferences getDevicePrefs(Context c) {
-        return c.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
-    }
-
-    private static final int getMyHotseatLayoutId(Context context) {
+    private static int getMyHotseatLayoutId(Context context) {
         return LauncherAppState.getIDP(context).numHotseatIcons <= 5
                 ? R.xml.dw_phone_hotseat
                 : R.xml.dw_tablet_hotseat;
@@ -396,9 +391,9 @@
         }
 
         @Override
-        protected HashMap<String, TagParser> getLayoutElementsMap() {
+        protected ArrayMap<String, TagParser> getLayoutElementsMap() {
             // Only allow shortcut parsers
-            HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+            ArrayMap<String, TagParser> parsers = new ArrayMap<>();
             parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
             parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
             parsers.put(TAG_RESOLVE, new ResolveParser());
@@ -410,7 +405,7 @@
      * {@link LayoutParserCallback} which adds items in empty hotseat spots.
      */
     private static class HotseatParserCallback implements LayoutParserCallback {
-        private final HashSet<String> mExisitingApps;
+        private final HashSet<String> mExistingApps;
         private final LongArrayMap<Object> mExistingItems;
         private final ArrayList<ContentProviderOperation> mOutOps;
         private final int mRequiredSize;
@@ -419,7 +414,7 @@
         HotseatParserCallback(
                 HashSet<String> existingApps, LongArrayMap<Object> existingItems,
                 ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
-            mExisitingApps = existingApps;
+            mExistingApps = existingApps;
             mExistingItems = existingItems;
             mOutOps = outOps;
             mRequiredSize = requiredSize;
@@ -444,11 +439,11 @@
                 return 0;
             }
             String pkg = getPackage(intent);
-            if (pkg == null || mExisitingApps.contains(pkg)) {
+            if (pkg == null || mExistingApps.contains(pkg)) {
                 // The item does not target an app or is already in hotseat.
                 return 0;
             }
-            mExisitingApps.add(pkg);
+            mExistingApps.add(pkg);
 
             // find next vacant spot.
             long screen = 0;
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 1758350..74373d3 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -19,15 +19,16 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 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.logging.FileLog;
 
 import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * A set of utility methods for Launcher DB used for DB updates and migration.
@@ -44,14 +45,14 @@
      * items are simply deleted.
      */
     public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
-        db.beginTransaction();
-        try {
+        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));
 
             if (screenIds.isEmpty()) {
                 // No update needed
+                t.commit();
                 return true;
             }
             if (screenIds.get(0) != 0) {
@@ -68,23 +69,20 @@
             }
 
             // Check if the first row is empty
-            try (Cursor c = db.query(Favorites.TABLE_NAME, null,
-                    "container = -100 and screen = 0 and cellY = 0", null, null, null, null)) {
-                if (c.getCount() == 0) {
-                    // First row is empty, no need to migrate.
-                    return true;
-                }
+            if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME,
+                    "container = -100 and screen = 0 and cellY = 0") == 0) {
+                // First row is empty, no need to migrate.
+                t.commit();
+                return true;
             }
 
             new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
                     .migrateScreen0();
-            db.setTransactionSuccessful();
+            t.commit();
             return true;
         } catch (Exception e) {
             Log.e(TAG, "Failed to update workspace size", e);
             return false;
-        } finally {
-            db.endTransaction();
         }
     }
 
@@ -104,19 +102,40 @@
      * Parses the cursor containing workspace screens table and returns the list of screen IDs
      */
     public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
-        ArrayList<Long> screenIds = new ArrayList<Long>();
         try {
-            final int idIndex = sc.getColumnIndexOrThrow(WorkspaceScreens._ID);
-            while (sc.moveToNext()) {
-                try {
-                    screenIds.add(sc.getLong(idIndex));
-                } catch (Exception e) {
-                    FileLog.d(TAG, "Invalid screen id", e);
-                }
-            }
+            return iterateCursor(sc,
+                    sc.getColumnIndexOrThrow(WorkspaceScreens._ID),
+                    new ArrayList<Long>());
         } finally {
             sc.close();
         }
-        return screenIds;
+    }
+
+    public static <T extends Collection<Long>> T iterateCursor(Cursor c, int columnIndex, T out) {
+        while (c.moveToNext()) {
+            out.add(c.getLong(columnIndex));
+        }
+        return out;
+    }
+
+    /**
+     * Utility class to simplify managing sqlite transactions
+     */
+    public static class SQLiteTransaction implements AutoCloseable {
+        private final SQLiteDatabase mDb;
+
+        public SQLiteTransaction(SQLiteDatabase db) {
+            mDb = db;
+            db.beginTransaction();
+        }
+
+        public void commit() {
+            mDb.setTransactionSuccessful();
+        }
+
+        @Override
+        public void close() {
+            mDb.endTransaction();
+        }
     }
 }
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
index 4addbfa..51890d1 100644
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -21,7 +21,6 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
-import android.util.Log;
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -31,7 +30,6 @@
 import com.android.launcher3.util.LongArrayMap;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 
 /**
  * An extension of {@link GridSizeMigrationTask} which migrates only one screen and
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index dc85aba..5230160 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.LogConfig;
 
 import java.io.InvalidObjectException;
@@ -47,16 +48,13 @@
 
     public static boolean performRestore(DatabaseHelper helper) {
         SQLiteDatabase db = helper.getWritableDatabase();
-        db.beginTransaction();
-        try {
+        try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             new RestoreDbTask().sanitizeDB(helper, db);
-            db.setTransactionSuccessful();
+            t.commit();
             return true;
         } catch (Exception e) {
             FileLog.e(TAG, "Failed to verify db", e);
             return false;
-        } finally {
-            db.endTransaction();
         }
     }
 
@@ -136,7 +134,7 @@
     }
 
     public static void setPending(Context context, boolean isPending) {
-        FileLog.d(TAG, "Restore data received through full backup");
+        FileLog.d(TAG, "Restore data received through full backup " + isPending);
         Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
     }
 }
diff --git a/src/com/android/launcher3/qsb/QsbBlockerView.java b/src/com/android/launcher3/qsb/QsbBlockerView.java
deleted file mode 100644
index 5379336..0000000
--- a/src/com/android/launcher3/qsb/QsbBlockerView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.qsb;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.Workspace.OnStateChangeListener;
-import com.android.launcher3.Workspace.State;
-
-/**
- * A simple view used to show the region blocked by QSB during drag and drop.
- */
-public class QsbBlockerView extends View implements OnStateChangeListener {
-
-    private static final int VISIBLE_ALPHA = 100;
-
-    private final Paint mBgPaint;
-
-    public QsbBlockerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mBgPaint.setColor(Color.WHITE);
-        mBgPaint.setAlpha(0);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        Workspace w = Launcher.getLauncher(getContext()).getWorkspace();
-        w.setOnStateChangeListener(this);
-        prepareStateChange(w.getState(), null);
-    }
-
-    @Override
-    public void prepareStateChange(State toState, AnimatorSet targetAnim) {
-        int finalAlpha = getAlphaForState(toState);
-        if (targetAnim == null) {
-            mBgPaint.setAlpha(finalAlpha);
-            invalidate();
-        } else {
-            ObjectAnimator anim = ObjectAnimator.ofArgb(mBgPaint, "alpha", finalAlpha);
-            anim.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                    invalidate();
-                }
-            });
-            targetAnim.play(anim);
-        }
-    }
-
-    private static int getAlphaForState(State state) {
-        switch (state) {
-            case SPRING_LOADED:
-            case OVERVIEW:
-            case OVERVIEW_HIDDEN:
-                return VISIBLE_ALPHA;
-        }
-        return 0;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        canvas.drawPaint(mBgPaint);
-    }
-}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 4dc3c1c..65acaa9 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -36,15 +36,18 @@
 
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
  * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
+ *
+ * Note: AppWidgetManagerCompat can be disabled using FeatureFlags. In QSB, we should use
+ * AppWidgetManager directly, so that it keeps working in that case.
  */
 public class QsbContainerView extends FrameLayout {
 
@@ -77,10 +80,15 @@
         private AppWidgetProviderInfo mWidgetInfo;
         private QsbWidgetHostView mQsb;
 
+        // We need to store the orientation here, due to a bug (b/64916689) that results in widgets
+        // being inflated in the wrong orientation.
+        private int mOrientation;
+
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             mQsbWidgetHost = new QsbWidgetHost(getActivity());
+            mOrientation = getContext().getResources().getConfiguration().orientation;
         }
 
         private FrameLayout mWrapper;
@@ -106,7 +114,7 @@
                 return QsbWidgetHostView.getDefaultView(container);
             }
 
-            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(activity);
+            AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
             InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
 
             Bundle opts = new Bundle();
@@ -129,7 +137,8 @@
                 }
 
                 widgetId = mQsbWidgetHost.allocateAppWidgetId();
-                isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
+                isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(
+                        widgetId, mWidgetInfo.getProfile(), mWidgetInfo.provider, opts);
                 if (!isWidgetBound) {
                     mQsbWidgetHost.deleteAppWidgetId(widgetId);
                     widgetId = -1;
@@ -190,7 +199,7 @@
         @Override
         public void onResume() {
             super.onResume();
-            if (mQsb != null && mQsb.isReinflateRequired()) {
+            if (mQsb != null && mQsb.isReinflateRequired(mOrientation)) {
                 rebindFragment();
             }
         }
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index 8b6fa16..a8a41f6 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -47,9 +47,9 @@
     }
 
 
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re-inflate is required if the orientation has changed since last inflation.
-        return mPreviousOrientation != getResources().getConfiguration().orientation;
+        return mPreviousOrientation != orientation;
     }
 
 
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index df7f695..f44f5c8 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -65,8 +65,10 @@
     }
 
     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();
+                && !info.isDisabled() && !isItemPromise;
     }
 
     public boolean wasLastCallSuccess() {
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
index d4db96d..5742d1d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutCache.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutCache.java
@@ -19,9 +19,8 @@
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.LruCache;
-
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -31,18 +30,15 @@
  */
 @TargetApi(Build.VERSION_CODES.N)
 public class ShortcutCache {
-    private static final String TAG = "ShortcutCache";
-    private static final boolean LOGD = false;
-
     private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
 
-    private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
+    private final LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
     // We always keep pinned shortcuts in the cache.
-    private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
+    private final ArrayMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
 
     public ShortcutCache() {
         mCachedShortcuts = new LruCache<>(CACHE_SIZE);
-        mPinnedShortcuts = new HashMap<>();
+        mPinnedShortcuts = new ArrayMap<>();
     }
 
     /**
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index ab8de6b..e9d2b50 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -23,10 +23,10 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
-import com.android.launcher3.graphics.HolographicOutlineHelper;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.HolographicOutlineHelper;
 
 /**
  * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 37047bb..9c91c87 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -18,15 +18,11 @@
 
 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.ItemInfo;
-import com.android.launcher3.compat.UserManagerCompat;
-
 /**
  * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
  *
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
index 5b3b02e..b4fa04e 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -18,10 +18,12 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Point;
-import android.support.v4.content.ContextCompat;
+import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -33,6 +35,7 @@
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
@@ -53,7 +56,10 @@
 public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener,
         View.OnTouchListener, LogContainerProvider {
 
+    private static final String TAG = "ShortcutsItem";
+
     private Launcher mLauncher;
+    private LinearLayout mContent;
     private LinearLayout mShortcutsLayout;
     private LinearLayout mSystemShortcutIcons;
     private final Point mIconShift = new Point();
@@ -61,6 +67,8 @@
     private final List<DeepShortcutView> mDeepShortcutViews = new ArrayList<>();
     private final List<View> mSystemShortcutViews = new ArrayList<>();
 
+    private int mHiddenShortcutsHeight;
+
     public ShortcutsItemView(Context context) {
         this(context, null, 0);
     }
@@ -78,7 +86,8 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mShortcutsLayout = findViewById(R.id.deep_shortcuts);
+        mContent = findViewById(R.id.content);
+        mShortcutsLayout = findViewById(R.id.shortcuts);
     }
 
     @Override
@@ -95,8 +104,8 @@
 
     @Override
     public boolean onLongClick(View v) {
-        // Return early if this is not initiated from a touch or not the correct view
-        if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
+        // Return early if not the correct view
+        if (!(v.getParent() instanceof DeepShortcutView)) return false;
         // Return early if global dragging is not enabled
         if (!mLauncher.isDraggingEnabled()) return false;
         // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
@@ -134,8 +143,9 @@
             // System shortcut icons are added to a header that is separate from the full shortcuts.
             if (mSystemShortcutIcons == null) {
                 mSystemShortcutIcons = (LinearLayout) mLauncher.getLayoutInflater().inflate(
-                        R.layout.system_shortcut_icons, mShortcutsLayout, false);
-                mShortcutsLayout.addView(mSystemShortcutIcons, 0);
+                        R.layout.system_shortcut_icons, mContent, false);
+                boolean iconsAreBelowShortcuts = mShortcutsLayout.getChildCount() > 0;
+                mContent.addView(mSystemShortcutIcons, iconsAreBelowShortcuts ? -1 : 0);
             }
             mSystemShortcutIcons.addView(shortcutView, index);
         } else {
@@ -166,6 +176,117 @@
     }
 
     /**
+     * Hides shortcuts until only {@param maxShortcuts} are showing. Also sets
+     * {@link #mHiddenShortcutsHeight} to be the amount of extra space that shortcuts will
+     * require when {@link #showAllShortcuts(boolean)} is called.
+     */
+    public void hideShortcuts(boolean hideFromTop, int maxShortcuts) {
+        // When shortcuts are shown, they get more space allocated to them.
+        final int oldHeight = mShortcutsLayout.getChildAt(0).getLayoutParams().height;
+        final int newHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
+        mHiddenShortcutsHeight = (newHeight - oldHeight) * mShortcutsLayout.getChildCount();
+
+        int numToHide = mShortcutsLayout.getChildCount() - maxShortcuts;
+        if (numToHide <= 0) {
+            return;
+        }
+        final int numShortcuts = mShortcutsLayout.getChildCount();
+        final int dir = hideFromTop ? 1 : -1;
+        for (int i = hideFromTop ? 0 : numShortcuts - 1; 0 <= i && i < numShortcuts; i += dir) {
+            View child = mShortcutsLayout.getChildAt(i);
+            if (child instanceof DeepShortcutView) {
+                mHiddenShortcutsHeight += child.getLayoutParams().height;
+                child.setVisibility(GONE);
+                int prev = i + dir;
+                if (!hideFromTop && 0 <= prev && prev < numShortcuts) {
+                    // When hiding views from the bottom, make sure to hide the last divider.
+                    mShortcutsLayout.getChildAt(prev).findViewById(R.id.divider).setVisibility(GONE);
+                }
+                numToHide--;
+                if (numToHide == 0) {
+                    break;
+                }
+            }
+        }
+    }
+
+    public int getHiddenShortcutsHeight() {
+        return mHiddenShortcutsHeight;
+    }
+
+    /**
+     * Sets all shortcuts in {@link #mShortcutsLayout} to VISIBLE, then creates an
+     * animation to reveal the newly shown shortcuts.
+     *
+     * @see #hideShortcuts(boolean, int)
+     */
+    public Animator showAllShortcuts(boolean showFromTop) {
+        // First set all the shortcuts to VISIBLE.
+        final int numShortcuts = mShortcutsLayout.getChildCount();
+        if (numShortcuts == 0) {
+            Log.w(TAG, "Tried to show all shortcuts but there were no shortcuts to show");
+            return null;
+        }
+        final int oldHeight = mShortcutsLayout.getChildAt(0).getLayoutParams().height;
+        final int newHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
+        for (int i = 0; i < numShortcuts; i++) {
+            DeepShortcutView view = (DeepShortcutView) mShortcutsLayout.getChildAt(i);
+            view.getLayoutParams().height = newHeight;
+            view.requestLayout();
+            view.setVisibility(VISIBLE);
+            if (i < numShortcuts - 1) {
+                view.findViewById(R.id.divider).setVisibility(VISIBLE);
+            }
+        }
+
+        // Now reveal the newly shown shortcuts.
+        AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+
+        if (showFromTop) {
+            // The new shortcuts pushed the original shortcuts down, but we want to animate them
+            // to that position. So we revert the translation and animate to the new.
+            animation.play(translateYFrom(mShortcutsLayout, -mHiddenShortcutsHeight));
+        } else if (mSystemShortcutIcons != null) {
+            // When adding the shortcuts from the bottom, things are a little trickier, since
+            // that means they push the icons header down. To account for this, we do the same
+            // translation trick as above, but on the header. Since this means leaving behind
+            // a blank area where the header was, we also need to clip the background.
+            animation.play(translateYFrom(mSystemShortcutIcons, -mHiddenShortcutsHeight));
+            // mPillRect is the bounds of this view before the new shortcuts were shown.
+            Rect backgroundStartRect = new Rect(mPillRect);
+            Rect backgroundEndRect = new Rect(mPillRect);
+            backgroundEndRect.bottom += mHiddenShortcutsHeight;
+            animation.play(new RoundedRectRevealOutlineProvider(getBackgroundRadius(),
+                    getBackgroundRadius(), backgroundStartRect, backgroundEndRect, mRoundedCorners)
+                    .createRevealAnimator(this, false));
+        }
+        for (int i = 0; i < numShortcuts; i++) {
+            // Animate each shortcut to its new height.
+            DeepShortcutView shortcut = (DeepShortcutView) mShortcutsLayout.getChildAt(i);
+            int heightDiff = newHeight - oldHeight;
+            int heightAdjustmentIndex = showFromTop ? numShortcuts - i - 1 : i;
+            int fromDir = showFromTop ? 1 : -1;
+            animation.play(translateYFrom(shortcut, heightDiff * heightAdjustmentIndex * fromDir));
+            // Make sure the text and icon stay centered in the shortcut.
+            animation.play(translateYFrom(shortcut.getBubbleText(), heightDiff / 2 * fromDir));
+            animation.play(translateYFrom(shortcut.getIconView(), heightDiff / 2 * fromDir));
+            // Scale icons back up to full size.
+            animation.play(LauncherAnimUtils.ofPropertyValuesHolder(shortcut.getIconView(),
+                    new PropertyListBuilder().scale(1f).build()));
+        }
+        return animation;
+    }
+
+    /**
+     * Animates the translationY of the view from the given offset to the view's current translation
+     * @return an Animator, which should be started by the caller.
+     */
+    private Animator translateYFrom(View v, int diff) {
+        float finalY = v.getTranslationY();
+        return ObjectAnimator.ofFloat(v, TRANSLATION_Y, finalY + diff, finalY);
+    }
+
+    /**
      * Adds a {@link SystemShortcut.Widgets} item if there are widgets for the given ItemInfo.
      */
     public void enableWidgetsIfExist(final BubbleTextView originalIcon) {
@@ -210,51 +331,6 @@
     }
 
     @Override
-    public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
-        AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
-        openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft));
-        for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
-            if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
-                continue;
-            }
-            DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
-            View deepShortcutIcon = shortcutView.getIconView();
-            deepShortcutIcon.setScaleX(0);
-            deepShortcutIcon.setScaleY(0);
-            openAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
-                    deepShortcutIcon, new PropertyListBuilder().scale(1).build()));
-        }
-        return openAnimation;
-    }
-
-    @Override
-    public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
-            long duration) {
-        AnimatorSet closeAnimation = LauncherAnimUtils.createAnimatorSet();
-        closeAnimation.play(super.createCloseAnimation(isContainerAboveIcon, pivotLeft, duration));
-        for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
-            if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
-                continue;
-            }
-            DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
-            View deepShortcutIcon = shortcutView.getIconView();
-            deepShortcutIcon.setScaleX(1);
-            deepShortcutIcon.setScaleY(1);
-            closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
-                    deepShortcutIcon, new PropertyListBuilder().scale(0).build()));
-        }
-        return closeAnimation;
-    }
-
-    @Override
-    public int getArrowColor(boolean isArrowAttachedToBottom) {
-        return ContextCompat.getColor(getContext(),
-                isArrowAttachedToBottom || mSystemShortcutIcons == null
-                        ? R.color.popup_background_color
-                        : R.color.popup_header_background_color);
-    }
-
-    @Override
     public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
             LauncherLogProto.Target targetParent) {
         target.itemType = LauncherLogProto.ItemType.DEEPSHORTCUT;
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index aedca8d..5cb8c4c 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -2,7 +2,6 @@
 
 import android.content.Intent;
 import android.graphics.Color;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
@@ -11,9 +10,8 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -200,12 +198,7 @@
         }
 
         @Override
-        public AllAppsSearchBarController getAllAppsSearchBarController() {
-            return null;
-        }
-
-        @Override
-        public List<ComponentKey> getPredictedApps() {
+        public List<ComponentKeyMapper<AppInfo>> getPredictedApps() {
             // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
             return new ArrayList<>();
         }
@@ -216,21 +209,11 @@
         }
 
         @Override
-        public void setLauncherSearchCallback(Object callbacks) {
-            // Do nothing
-        }
-
-        @Override
         public void onAttachedToWindow() {
         }
 
         @Override
         public void onDetachedFromWindow() {
         }
-
-        @Override
-        public boolean shouldShowDiscoveryBounce() {
-            return false;
-        }
     }
 }
diff --git a/src/com/android/launcher3/touch/OverScroll.java b/src/com/android/launcher3/touch/OverScroll.java
new file mode 100644
index 0000000..dc801ec
--- /dev/null
+++ b/src/com/android/launcher3/touch/OverScroll.java
@@ -0,0 +1,55 @@
+/*
+ * 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.touch;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ */
+public class OverScroll {
+
+    private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+    /**
+     * This curve determines how the effect of scrolling over the limits of the page diminishes
+     * as the user pulls further and further from the bounds
+     *
+     * @param f The percentage of how much the user has overscrolled.
+     * @return A transformed percentage based on the influence curve.
+     */
+    private static float overScrollInfluenceCurve(float f) {
+        f -= 1.0f;
+        return f * f * f + 1.0f;
+    }
+
+    /**
+     * @param amount The original amount overscrolled.
+     * @param max The maximum amount that the View can overscroll.
+     * @return The dampened overscroll amount.
+     */
+    public static int dampedScroll(float amount, int max) {
+        if (Float.compare(amount, 0) == 0) return 0;
+
+        float f = amount / max;
+        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+        // Clamp this factor, f, to -1 < f < 1
+        if (Math.abs(f) >= 1) {
+            f /= Math.abs(f);
+        }
+
+        return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+    }
+}
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
new file mode 100644
index 0000000..be4648e
--- /dev/null
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -0,0 +1,372 @@
+/*
+ * 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.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+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.ViewConfiguration;
+import android.view.animation.Interpolator;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
+ */
+public class SwipeDetector {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "SwipeDetector";
+
+    private int mScrollConditions;
+    public static final int DIRECTION_POSITIVE = 1 << 0;
+    public static final int DIRECTION_NEGATIVE = 1 << 1;
+    public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+    private static final float ANIMATION_DURATION = 1200;
+    private static final float FAST_FLING_PX_MS = 10;
+
+    protected int mActivePointerId = INVALID_POINTER_ID;
+
+    /**
+     * The minimum release velocity in pixels per millisecond that triggers fling..
+     */
+    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;
+
+    enum ScrollState {
+        IDLE,
+        DRAGGING,      // onDragStart, onDrag
+        SETTLING       // onDragEnd
+    }
+
+    public static abstract class Direction {
+
+        abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+
+        /**
+         * Distance in pixels a touch can wander before we think the user is scrolling.
+         */
+        abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+    }
+
+    public static final Direction VERTICAL = new Direction() {
+
+        @Override
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+            return ev.getY(pointerIndex) - refPoint.y;
+        }
+
+        @Override
+        float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+            return Math.abs(ev.getX(pointerIndex) - downPos.x);
+        }
+    };
+
+    public static final Direction HORIZONTAL = new Direction() {
+
+        @Override
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+            return ev.getX(pointerIndex) - refPoint.x;
+        }
+
+        @Override
+        float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+            return Math.abs(ev.getY(pointerIndex) - downPos.y);
+        }
+    };
+
+    //------------------- ScrollState transition diagram -----------------------------------
+    //
+    // IDLE ->      (mDisplacement > mTouchSlop) -> DRAGGING
+    // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+    // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+    // SETTLING -> (View settled) -> IDLE
+
+    private void setState(ScrollState newState) {
+        if (DBG) {
+            Log.d(TAG, "setState:" + mState + "->" + newState);
+        }
+        // onDragStart and onDragEnd is reported ONLY on state transition
+        if (newState == ScrollState.DRAGGING) {
+            initializeDragging();
+            if (mState == ScrollState.IDLE) {
+                reportDragStart(false /* recatch */);
+            } else if (mState == ScrollState.SETTLING) {
+                reportDragStart(true /* recatch */);
+            }
+        }
+        if (newState == ScrollState.SETTLING) {
+            reportDragEnd();
+        }
+
+        mState = newState;
+    }
+
+    public boolean isDraggingOrSettling() {
+        return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+    }
+
+    /**
+     * There's no touch and there's no animation.
+     */
+    public boolean isIdleState() {
+        return mState == ScrollState.IDLE;
+    }
+
+    public boolean isSettlingState() {
+        return mState == ScrollState.SETTLING;
+    }
+
+    public boolean isDraggingState() {
+        return mState == ScrollState.DRAGGING;
+    }
+
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+    private final Direction mDir;
+
+    private final float mTouchSlop;
+
+    /* Client of this gesture detector can register a callback. */
+    private final Listener mListener;
+
+    private long mCurrentMillis;
+
+    private float mVelocity;
+    private float mLastDisplacement;
+    private float mDisplacement;
+
+    private float mSubtractDisplacement;
+    private boolean mIgnoreSlopWhenSettling;
+
+    public interface Listener {
+        void onDragStart(boolean start);
+
+        boolean onDrag(float displacement, float velocity);
+
+        void onDragEnd(float velocity, boolean fling);
+    }
+
+    public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+        this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+    }
+
+    @VisibleForTesting
+    protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
+        mTouchSlop = touchSlope;
+        mListener = l;
+        mDir = dir;
+    }
+
+    public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+        mScrollConditions = scrollDirectionFlags;
+        mIgnoreSlopWhenSettling = ignoreSlop;
+    }
+
+    private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+        // reject cases where the angle or slop condition is not met.
+        if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+                > Math.abs(mDisplacement)) {
+            return false;
+        }
+
+        // Check if the client is interested in scroll in current direction.
+        if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
+                ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
+            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);
+                }
+                break;
+            //case MotionEvent.ACTION_POINTER_DOWN:
+            case MotionEvent.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 MotionEvent.ACTION_MOVE:
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == INVALID_POINTER_ID) {
+                    break;
+                }
+                mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+                computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
+                        ev.getEventTime());
+
+                // handle state and listener calls.
+                if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+                    setState(ScrollState.DRAGGING);
+                }
+                if (mState == ScrollState.DRAGGING) {
+                    reportDragging();
+                }
+                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                // These are synthetic events and there is no need to update internal values.
+                if (mState == ScrollState.DRAGGING) {
+                    setState(ScrollState.SETTLING);
+                }
+                break;
+            default:
+                break;
+        }
+        return true;
+    }
+
+    public void finishedScrolling() {
+        setState(ScrollState.IDLE);
+    }
+
+    private boolean reportDragStart(boolean recatch) {
+        mListener.onDragStart(!recatch);
+        if (DBG) {
+            Log.d(TAG, "onDragStart recatch:" + recatch);
+        }
+        return true;
+    }
+
+    private void initializeDragging() {
+        if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+            mSubtractDisplacement = 0;
+        }
+        if (mDisplacement > 0) {
+            mSubtractDisplacement = mTouchSlop;
+        } else {
+            mSubtractDisplacement = -mTouchSlop;
+        }
+    }
+
+    private boolean reportDragging() {
+        if (mDisplacement != mLastDisplacement) {
+            if (DBG) {
+                Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
+                        mDisplacement, mVelocity));
+            }
+
+            mLastDisplacement = mDisplacement;
+            return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+        }
+        return true;
+    }
+
+    private void reportDragEnd() {
+        if (DBG) {
+            Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
+                    mDisplacement, mVelocity));
+        }
+        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
+     */
+    private static float interpolate(float from, float to, float alpha) {
+        return (1.0f - alpha) * from + alpha * to;
+    }
+
+    public static long calculateDuration(float velocity, float progressNeeded) {
+        // TODO: make these values constants after tuning.
+        float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+        float travelDistance = Math.max(0.2f, progressNeeded);
+        long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+        if (DBG) {
+            Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+        }
+        return duration;
+    }
+
+    public static class ScrollInterpolator implements Interpolator {
+
+        boolean mSteeper;
+
+        public void setVelocityAtZero(float velocity) {
+            mSteeper = velocity > FAST_FLING_PX_MS;
+        }
+
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            float output = t * t * t;
+            if (mSteeper) {
+                output *= t * t; // Make interpolation initial slope steeper
+            }
+            return output + 1;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
deleted file mode 100644
index 314b4c0..0000000
--- a/src/com/android/launcher3/util/CachedPackageTracker.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherActivityInfo;
-import android.os.UserHandle;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
-import com.android.launcher3.compat.UserManagerCompat;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Utility class to track list of installed packages. It persists the list so that apps
- * installed/uninstalled while Launcher was dead can also be handled properly.
- */
-public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {
-
-    protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
-
-    protected final SharedPreferences mPrefs;
-    protected final UserManagerCompat mUserManager;
-    protected final LauncherAppsCompat mLauncherApps;
-
-    public CachedPackageTracker(Context context, String preferenceFileName) {
-        mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
-        mUserManager = UserManagerCompat.getInstance(context);
-        mLauncherApps = LauncherAppsCompat.getInstance(context);
-    }
-
-    /**
-     * Checks the list of user apps, and generates package event accordingly.
-     * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
-     */
-    public void processUserApps(List<LauncherActivityInfo> apps, UserHandle user) {
-        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
-        HashSet<String> oldPackageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
-
-        HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
-        HashSet<String> newPackageSet = new HashSet<>();
-        ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
-
-        for (LauncherActivityInfo info : apps) {
-            String packageName = info.getComponentName().getPackageName();
-            newPackageSet.add(packageName);
-            packagesRemoved.remove(packageName);
-
-            if (!oldPackageSet.contains(packageName)) {
-                oldPackageSet.add(packageName);
-                packagesAdded.add(new LauncherActivityInstallInfo(
-                        info, info.getFirstInstallTime()));
-            }
-        }
-
-        if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
-            mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();
-
-            if (!packagesAdded.isEmpty()) {
-                Collections.sort(packagesAdded);
-                onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
-            }
-
-            if (!packagesRemoved.isEmpty()) {
-                for (String pkg : packagesRemoved) {
-                    onLauncherPackageRemoved(pkg, user);
-                }
-            }
-        }
-    }
-
-    /**
-     * Reads the list of user apps which have already been processed.
-     * @return false if the list didn't exist, true otherwise
-     */
-    private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
-        Set<String> userApps = mPrefs.getStringSet(prefKey, null);
-        if (userApps == null) {
-            return false;
-        } else {
-            outExistingApps.addAll(userApps);
-            return true;
-        }
-    }
-
-    @Override
-    public void onPackageRemoved(String packageName, UserHandle user) {
-        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
-        HashSet<String> packageSet = new HashSet<>();
-        if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
-            mPrefs.edit().putStringSet(prefKey, packageSet).apply();
-        }
-
-        onLauncherPackageRemoved(packageName, user);
-    }
-
-    @Override
-    public void onPackageAdded(String packageName, UserHandle user) {
-        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
-        HashSet<String> packageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(packageSet, prefKey);
-        if (!packageSet.contains(packageName)) {
-            List<LauncherActivityInfo> activities =
-                    mLauncherApps.getActivityList(packageName, user);
-            if (!activities.isEmpty()) {
-                LauncherActivityInfo activityInfo = activities.get(0);
-
-                packageSet.add(packageName);
-                mPrefs.edit().putStringSet(prefKey, packageSet).apply();
-                onLauncherAppsAdded(Arrays.asList(
-                        new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
-                        user, userAppsExisted);
-            }
-        }
-    }
-
-    @Override
-    public void onPackageChanged(String packageName, UserHandle user) { }
-
-    @Override
-    public void onPackagesAvailable(
-            String[] packageNames, UserHandle user, boolean replacing) { }
-
-    @Override
-    public void onPackagesUnavailable(
-            String[] packageNames, UserHandle user, boolean replacing) { }
-
-    @Override
-    public void onPackagesSuspended(String[] packageNames, UserHandle user) { }
-
-    @Override
-    public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { }
-
-    /**
-     * Called when new launcher apps are added.
-     * @param apps list of newly added activities. Only one entry per package is sent.
-     * @param user the user for this event. All activities in {@param apps} will belong to
-     *             the same user.
-     * @param userAppsExisted false if the list was processed for the first time, like in case
-     *                        when Launcher was newly installed or a new user was added.
-     */
-    protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
-            UserHandle user, boolean userAppsExisted);
-
-    /**
-     * Called when apps are removed from the system.
-     */
-    protected abstract void onLauncherPackageRemoved(String packageName, UserHandle user);
-
-    public static class LauncherActivityInstallInfo
-            implements Comparable<LauncherActivityInstallInfo> {
-        public final LauncherActivityInfo info;
-        public final long installTime;
-
-        public LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime) {
-            this.info = info;
-            this.installTime = installTime;
-        }
-
-        @Override
-        public int compareTo(LauncherActivityInstallInfo another) {
-            return Utilities.longCompare(installTime, another.installTime);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
new file mode 100644
index 0000000..916176a
--- /dev/null
+++ b/src/com/android/launcher3/util/ComponentKeyMapper.java
@@ -0,0 +1,50 @@
+package com.android.launcher3.util;
+
+/**
+ * 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.
+ */
+
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ComponentKeyMapper<T> {
+
+    protected final ComponentKey mComponentKey;
+
+    public ComponentKeyMapper(ComponentKey key) {
+        this.mComponentKey = key;
+    }
+
+    public @Nullable T getItem(Map<ComponentKey, T> map) {
+        return map.get(mComponentKey);
+    }
+
+    public String getPackage() {
+        return mComponentKey.componentName.getPackageName();
+    }
+
+    public String getComponentClass() {
+        return mComponentKey.componentName.getClassName();
+    }
+
+    @Override
+    public String toString() {
+        return mComponentKey.toString();
+    }
+
+}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index afc45fe..b80e94d 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -23,7 +23,6 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.config.FeatureFlags;
 
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
new file mode 100644
index 0000000..77c21fe
--- /dev/null
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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 java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Supports various IO utility functions
+ */
+public class IOUtils {
+
+    private static final int BUF_SIZE = 0x1000; // 4K
+
+    public static byte[] toByteArray(File file) throws IOException {
+        try (InputStream in = new FileInputStream(file)) {
+            return toByteArray(in);
+        }
+    }
+
+    public static byte[] toByteArray(InputStream in) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        copy(in, out);
+        return out.toByteArray();
+    }
+
+    public static long copy(InputStream from, OutputStream to) throws IOException {
+        byte[] buf = new byte[BUF_SIZE];
+        long total = 0;
+        int r;
+        while ((r = from.read(buf)) != -1) {
+            to.write(buf, 0, r);
+            total += r;
+        }
+        return total;
+    }
+}
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
new file mode 100644
index 0000000..e60d768
--- /dev/null
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.content.Context;
+import android.content.pm.ApplicationInfo;
+
+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 static InstantAppResolver newInstance(Context context) {
+        return Utilities.getOverrideObject(
+                InstantAppResolver.class, context, R.string.instant_app_resolver_class);
+    }
+
+    public boolean isInstantApp(ApplicationInfo info) {
+        return false;
+    }
+
+    public List<ApplicationInfo> getInstantApps() {
+        return Collections.emptyList();
+    }
+}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 42de284..18787b6 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.os.UserHandle;
+import android.util.SparseLongArray;
 
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
@@ -66,6 +67,32 @@
         return filtered;
     }
 
+    /**
+     * 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);
+            }
+        };
+    }
+
+    /**
+     * 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);
+            }
+        };
+    }
+
     public static ItemInfoMatcher ofUser(final UserHandle user) {
         return new ItemInfoMatcher() {
             @Override
@@ -104,4 +131,14 @@
             }
         };
     }
+
+    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);
+            }
+        };
+    }
 }
diff --git a/src/com/android/launcher3/util/LauncherEdgeEffect.java b/src/com/android/launcher3/util/LauncherEdgeEffect.java
deleted file mode 100644
index 3e3b255..0000000
--- a/src/com/android/launcher3/util/LauncherEdgeEffect.java
+++ /dev/null
@@ -1,365 +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.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-/**
- * This class differs from the framework {@link android.widget.EdgeEffect}:
- *   1) It does not use PorterDuffXfermode
- *   2) The width to radius factor is smaller (0.5 instead of 0.75)
- */
-public class LauncherEdgeEffect {
-
-    // Time it will take the effect to fully recede in ms
-    private static final int RECEDE_TIME = 600;
-
-    // Time it will take before a pulled glow begins receding in ms
-    private static final int PULL_TIME = 167;
-
-    // Time it will take in ms for a pulled glow to decay to partial strength before release
-    private static final int PULL_DECAY_TIME = 2000;
-
-    private static final float MAX_ALPHA = 0.5f;
-
-    private static final float MAX_GLOW_SCALE = 2.f;
-
-    private static final float PULL_GLOW_BEGIN = 0.f;
-
-    // Minimum velocity that will be absorbed
-    private static final int MIN_VELOCITY = 100;
-    // Maximum velocity, clamps at this value
-    private static final int MAX_VELOCITY = 10000;
-
-    private static final float EPSILON = 0.001f;
-
-    private static final double ANGLE = Math.PI / 6;
-    private static final float SIN = (float) Math.sin(ANGLE);
-    private static final float COS = (float) Math.cos(ANGLE);
-
-    private float mGlowAlpha;
-    private float mGlowScaleY;
-
-    private float mGlowAlphaStart;
-    private float mGlowAlphaFinish;
-    private float mGlowScaleYStart;
-    private float mGlowScaleYFinish;
-
-    private long mStartTime;
-    private float mDuration;
-
-    private final Interpolator mInterpolator;
-
-    private static final int STATE_IDLE = 0;
-    private static final int STATE_PULL = 1;
-    private static final int STATE_ABSORB = 2;
-    private static final int STATE_RECEDE = 3;
-    private static final int STATE_PULL_DECAY = 4;
-
-    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
-
-    private static final int VELOCITY_GLOW_FACTOR = 6;
-
-    private int mState = STATE_IDLE;
-
-    private float mPullDistance;
-
-    private final Rect mBounds = new Rect();
-    private final Paint mPaint = new Paint();
-    private float mRadius;
-    private float mBaseGlowScale;
-    private float mDisplacement = 0.5f;
-    private float mTargetDisplacement = 0.5f;
-
-    /**
-     * Construct a new EdgeEffect with a theme appropriate for the provided context.
-     */
-    public LauncherEdgeEffect() {
-        mPaint.setAntiAlias(true);
-        mPaint.setStyle(Paint.Style.FILL);
-        mInterpolator = new DecelerateInterpolator();
-    }
-
-    /**
-     * Set the size of this edge effect in pixels.
-     *
-     * @param width Effect width in pixels
-     * @param height Effect height in pixels
-     */
-    public void setSize(int width, int height) {
-        final float r = width * 0.5f / SIN;
-        final float y = COS * r;
-        final float h = r - y;
-        final float or = height * 0.75f / SIN;
-        final float oy = COS * or;
-        final float oh = or - oy;
-
-        mRadius = r;
-        mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
-
-        mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
-    }
-
-    /**
-     * Reports if this EdgeEffect's animation is finished. If this method returns false
-     * after a call to {@link #draw(Canvas)} the host widget should schedule another
-     * drawing pass to continue the animation.
-     *
-     * @return true if animation is finished, false if drawing should continue on the next frame.
-     */
-    public boolean isFinished() {
-        return mState == STATE_IDLE;
-    }
-
-    /**
-     * Immediately finish the current animation.
-     * After this call {@link #isFinished()} will return true.
-     */
-    public void finish() {
-        mState = STATE_IDLE;
-    }
-
-    /**
-     * A view should call this when content is pulled away from an edge by the user.
-     * This will update the state of the current visual effect and its associated animation.
-     * The host view should always {@link android.view.View#invalidate()} after this
-     * and draw the results accordingly.
-     *
-     * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
-     * of the pull point is known.</p>
-     *
-     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
-     *                      1.f (full length of the view) or negative values to express change
-     *                      back toward the edge reached to initiate the effect.
-     */
-    public void onPull(float deltaDistance) {
-        onPull(deltaDistance, 0.5f);
-    }
-
-    /**
-     * A view should call this when content is pulled away from an edge by the user.
-     * This will update the state of the current visual effect and its associated animation.
-     * The host view should always {@link android.view.View#invalidate()} after this
-     * and draw the results accordingly.
-     *
-     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
-     *                      1.f (full length of the view) or negative values to express change
-     *                      back toward the edge reached to initiate the effect.
-     * @param displacement The displacement from the starting side of the effect of the point
-     *                     initiating the pull. In the case of touch this is the finger position.
-     *                     Values may be from 0-1.
-     */
-    public void onPull(float deltaDistance, float displacement) {
-        final long now = AnimationUtils.currentAnimationTimeMillis();
-        mTargetDisplacement = displacement;
-        if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
-            return;
-        }
-        if (mState != STATE_PULL) {
-            mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
-        }
-        mState = STATE_PULL;
-
-        mStartTime = now;
-        mDuration = PULL_TIME;
-
-        mPullDistance += deltaDistance;
-
-        final float absdd = Math.abs(deltaDistance);
-        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
-                mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
-
-        if (mPullDistance == 0) {
-            mGlowScaleY = mGlowScaleYStart = 0;
-        } else {
-            final float scale = (float) (Math.max(0, 1 - 1 /
-                    Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d);
-
-            mGlowScaleY = mGlowScaleYStart = scale;
-        }
-
-        mGlowAlphaFinish = mGlowAlpha;
-        mGlowScaleYFinish = mGlowScaleY;
-    }
-
-    /**
-     * Call when the object is released after being pulled.
-     * This will begin the "decay" phase of the effect. After calling this method
-     * the host view should {@link android.view.View#invalidate()} and thereby
-     * draw the results accordingly.
-     */
-    public void onRelease() {
-        mPullDistance = 0;
-
-        if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
-            return;
-        }
-
-        mState = STATE_RECEDE;
-        mGlowAlphaStart = mGlowAlpha;
-        mGlowScaleYStart = mGlowScaleY;
-
-        mGlowAlphaFinish = 0.f;
-        mGlowScaleYFinish = 0.f;
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = RECEDE_TIME;
-    }
-
-    /**
-     * Call when the effect absorbs an impact at the given velocity.
-     * Used when a fling reaches the scroll boundary.
-     *
-     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
-     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
-     * to use here.</p>
-     *
-     * @param velocity Velocity at impact in pixels per second.
-     */
-    public void onAbsorb(int velocity) {
-        mState = STATE_ABSORB;
-        velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = 0.15f + (velocity * 0.02f);
-
-        // The glow depends more on the velocity, and therefore starts out
-        // nearly invisible.
-        mGlowAlphaStart = 0.3f;
-        mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
-
-
-        // Growth for the size of the glow should be quadratic to properly
-        // respond
-        // to a user's scrolling speed. The faster the scrolling speed, the more
-        // intense the effect should be for both the size and the saturation.
-        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
-        // Alpha should change for the glow as well as size.
-        mGlowAlphaFinish = Math.max(
-                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
-        mTargetDisplacement = 0.5f;
-    }
-
-    /**
-     * Set the color of this edge effect in argb.
-     *
-     * @param color Color in argb
-     */
-    public void setColor(int color) {
-        mPaint.setColor(color);
-    }
-
-    /**
-     * Return the color of this edge effect in argb.
-     * @return The color of this edge effect in argb
-     */
-    public int getColor() {
-        return mPaint.getColor();
-    }
-
-    /**
-     * Draw into the provided canvas. Assumes that the canvas has been rotated
-     * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
-     * 1.f of height.
-     *
-     * @param canvas Canvas to draw into
-     * @return true if drawing should continue beyond this frame to continue the
-     *         animation
-     */
-    public boolean draw(Canvas canvas) {
-        update();
-
-        final float centerX = mBounds.centerX();
-        final float centerY = mBounds.height() - mRadius;
-
-        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
-
-        final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
-        float translateX = mBounds.width() * displacement / 2;
-        mPaint.setAlpha((int) (0xff * mGlowAlpha));
-        canvas.drawCircle(centerX + translateX, centerY, mRadius, mPaint);
-
-        boolean oneLastFrame = false;
-        if (mState == STATE_RECEDE && mGlowScaleY == 0) {
-            mState = STATE_IDLE;
-            oneLastFrame = true;
-        }
-
-        return mState != STATE_IDLE || oneLastFrame;
-    }
-
-    /**
-     * Return the maximum height that the edge effect will be drawn at given the original
-     * {@link #setSize(int, int) input size}.
-     * @return The maximum height of the edge effect
-     */
-    public int getMaxHeight() {
-        return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
-    }
-
-    private void update() {
-        final long time = AnimationUtils.currentAnimationTimeMillis();
-        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
-
-        final float interp = mInterpolator.getInterpolation(t);
-
-        mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
-        mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
-        mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
-
-        if (t >= 1.f - EPSILON) {
-            switch (mState) {
-                case STATE_ABSORB:
-                    mState = STATE_RECEDE;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = RECEDE_TIME;
-
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After absorb, the glow should fade to nothing.
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL:
-                    mState = STATE_PULL_DECAY;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = PULL_DECAY_TIME;
-
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After pull, the glow should fade to nothing.
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL_DECAY:
-                    mState = STATE_RECEDE;
-                    break;
-                case STATE_RECEDE:
-                    mState = STATE_IDLE;
-                    break;
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/LooperExecuter.java b/src/com/android/launcher3/util/LooperExecutor.java
similarity index 94%
rename from src/com/android/launcher3/util/LooperExecuter.java
rename to src/com/android/launcher3/util/LooperExecutor.java
index 4db999b..5b7c20b 100644
--- a/src/com/android/launcher3/util/LooperExecuter.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -25,11 +25,11 @@
 /**
  * Extension of {@link AbstractExecutorService} which executed on a provided looper.
  */
-public class LooperExecuter extends AbstractExecutorService {
+public class LooperExecutor extends AbstractExecutorService {
 
     private final Handler mHandler;
 
-    public LooperExecuter(Looper looper) {
+    public LooperExecutor(Looper looper) {
         mHandler = new Handler(looper);
     }
 
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
new file mode 100644
index 0000000..35cac14
--- /dev/null
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -0,0 +1,71 @@
+/*
+ * 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.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility class to block execution until the UI looper is idle.
+ */
+public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
+
+    private final Object mLock;
+
+    private boolean mIsLocked;
+
+    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);
+        }
+    }
+
+    @Override
+    public void run() {
+        Looper.myQueue().addIdleHandler(this);
+    }
+
+    @Override
+    public boolean queueIdle() {
+        synchronized (mLock) {
+            mIsLocked = false;
+            mLock.notify();
+        }
+        return false;
+    }
+
+    public boolean awaitLocked(long ms) {
+        if (mIsLocked) {
+            try {
+                // Just in case mFlushingWorkerThread changes but we aren't woken up,
+                // wait no longer than 1sec at a time
+                mLock.wait(ms);
+            } catch (InterruptedException ex) {
+                // Ignore
+            }
+        }
+        return mIsLocked;
+    }
+}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index ce603c4..009aee7 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -19,23 +19,23 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.LauncherActivityInfo;
+import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.v4.os.BuildCompat;
 
-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.LauncherAppState;
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.ModelWriter;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -47,11 +47,6 @@
  */
 public class ManagedProfileHeuristic {
 
-    /**
-     * Maintain a set of packages installed per user.
-     */
-    private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
-
     private static final String USER_FOLDER_ID_PREFIX = "user_folder_";
 
     /**
@@ -59,165 +54,154 @@
      */
     private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
 
-    public static ManagedProfileHeuristic get(Context context, UserHandle user) {
-        if (!Process.myUserHandle().equals(user)) {
-            return new ManagedProfileHeuristic(context, user);
-        }
-        return null;
-    }
-
-    private final Context mContext;
-    private final LauncherModel mModel;
-    private final UserHandle mUser;
-    private final IconCache mIconCache;
-    private final boolean mAddIconsToHomescreen;
-
-    private ManagedProfileHeuristic(Context context, UserHandle user) {
-        mContext = context;
-        mUser = user;
-        mModel = LauncherAppState.getInstance(context).getModel();
-        mIconCache = LauncherAppState.getInstance(context).getIconCache();
-        mAddIconsToHomescreen =
-                !BuildCompat.isAtLeastO() || SessionCommitReceiver.isEnabled(context);
-    }
-
-    public void processPackageRemoved(String[] packages) {
-        Preconditions.assertWorkerThread();
-        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
-        for (String pkg : packages) {
-            handler.onPackageRemoved(pkg, mUser);
-        }
-    }
-
-    public void processPackageAdd(String[] packages) {
-        Preconditions.assertWorkerThread();
-        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
-        for (String pkg : packages) {
-            handler.onPackageAdded(pkg, mUser);
-        }
-    }
-
-    public void processUserApps(List<LauncherActivityInfo> apps) {
-        Preconditions.assertWorkerThread();
-        new ManagedProfilePackageHandler().processUserApps(apps, mUser);
-    }
-
-    private class ManagedProfilePackageHandler extends CachedPackageTracker {
-
-        private ManagedProfilePackageHandler() {
-            super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY);
+    public static void onAllAppsLoaded(final Context context,
+            List<LauncherActivityInfo> apps, UserHandle user) {
+        if (Process.myUserHandle().equals(user)) {
+            return;
         }
 
-        protected void onLauncherAppsAdded(
-                List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted) {
-            ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>();
-            ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>();
+        UserFolderInfo ufi = new UserFolderInfo(context, user, null);
+        // We only handle folder creation once. Later icon additions are handled using package
+        // or session events.
+        if (ufi.folderAlreadyCreated) {
+            return;
+        }
 
-            int count = apps.size();
-            long folderCreationTime =
-                    mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+        if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) {
+            // Just mark the folder id preference to avoid new folder creation later.
+            ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
+            return;
+        }
 
-            boolean quietModeEnabled = UserManagerCompat.getInstance(mContext)
-                    .isQuietModeEnabled(user);
-            for (int i = 0; i < count; i++) {
-                LauncherActivityInstallInfo info = apps.get(i);
-                AppInfo appInfo = new AppInfo(info.info, user, quietModeEnabled);
-                mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */);
-                ShortcutInfo si = appInfo.makeShortcut();
-                ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
-            }
-
-            finalizeWorkFolder(user, workFolderApps, homescreenApps);
-
-            // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
-            // getting filled with the managed user apps, when it start with a fresh DB (or after
-            // a very long time).
-            if (userAppsExisted && !homescreenApps.isEmpty() && mAddIconsToHomescreen) {
-                mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps));
+        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_BULK_ADD);
+        for (LauncherActivityInfo app : apps) {
+            // Queue all items which should go in the work folder.
+            if (app.getFirstInstallTime() < ufi.addIconToFolderTime) {
+                InstallShortcutReceiver.queueActivityInfo(app, context);
             }
         }
+        // Post the queue update on next frame, so that the loader gets finished.
+        new Handler(LauncherModel.getWorkerLooper()).post(new Runnable() {
+            @Override
+            public void run() {
+                InstallShortcutReceiver.disableAndFlushInstallQueue(
+                        InstallShortcutReceiver.FLAG_BULK_ADD, context);
+            }
+        });
+    }
 
-        @Override
-        protected void onLauncherPackageRemoved(String packageName, UserHandle user) {
+
+    /**
+     * Utility class to help workspace icon addition.
+     */
+    public static class UserFolderInfo {
+
+        final ArrayList<ShortcutInfo> pendingShortcuts = new ArrayList<>();
+
+        final UserHandle user;
+
+        final long userSerial;
+        // Time until which icons will be added to folder instead.
+        final long addIconToFolderTime;
+
+        final String folderIdKey;
+        final SharedPreferences prefs;
+
+        final boolean folderAlreadyCreated;
+        final FolderInfo folderInfo;
+
+        boolean folderPendingAddition;
+
+        public UserFolderInfo(Context context, UserHandle user, BgDataModel dataModel) {
+            this.user = user;
+
+            UserManagerCompat um = UserManagerCompat.getInstance(context);
+            userSerial = um.getSerialNumberForUser(user);
+            addIconToFolderTime = um.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+
+            folderIdKey = USER_FOLDER_ID_PREFIX + userSerial;
+            prefs = prefs(context);
+
+            folderAlreadyCreated = prefs.contains(folderIdKey);
+            if (dataModel != null) {
+                if (folderAlreadyCreated) {
+                    long folderId = prefs.getLong(folderIdKey, ItemInfo.NO_ID);
+                    folderInfo = dataModel.folders.get(folderId);
+                } else {
+                    folderInfo = new FolderInfo();
+                    folderInfo.title = context.getText(R.string.work_folder_name);
+                    folderInfo.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+                    folderPendingAddition = true;
+                }
+            } else {
+                folderInfo = null;
+            }
         }
 
         /**
-         * Adds and binds shortcuts marked to be added to the work folder.
+         * Returns the ItemInfo which should be added to the workspace. In case the the provided
+         * {@link ShortcutInfo} or a wrapped {@link FolderInfo} or null.
          */
-        private void finalizeWorkFolder(
-                UserHandle user, final ArrayList<ShortcutInfo> workFolderApps,
-                ArrayList<ShortcutInfo> homescreenApps) {
-            if (workFolderApps.isEmpty()) {
+        public ItemInfo convertToWorkspaceItem(
+                ShortcutInfo shortcut, LauncherActivityInfo activityInfo) {
+            if (activityInfo.getFirstInstallTime() >= addIconToFolderTime) {
+                return shortcut;
+            }
+
+            if (folderAlreadyCreated) {
+                if (folderInfo == null) {
+                    // Work folder was deleted by user, add icon to home screen.
+                    return shortcut;
+                } else {
+                    // Add item to work folder instead. Nothing needs to be added
+                    // on the homescreen.
+                    pendingShortcuts.add(shortcut);
+                    return null;
+                }
+            }
+
+            pendingShortcuts.add(shortcut);
+            folderInfo.add(shortcut, false);
+            if (folderPendingAddition) {
+                folderPendingAddition = false;
+                return folderInfo;
+            } else {
+                // WorkFolder already requested to be added. Nothing new needs to be added.
+                return null;
+            }
+        }
+
+        public void applyPendingState(ModelWriter writer) {
+            if (folderInfo == null) {
                 return;
             }
-            // Try to get a work folder.
-            String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user);
-            if (!mAddIconsToHomescreen) {
-                if (!mPrefs.contains(folderIdKey)) {
-                    // Just mark the folder id preference to avoid new folder creation later.
-                    mPrefs.edit().putLong(folderIdKey, -1).apply();
-                }
-                return;
+
+            int startingRank = 0;
+            if (folderAlreadyCreated) {
+                startingRank = folderInfo.contents.size();
             }
-            if (mPrefs.contains(folderIdKey)) {
-                long folderId = mPrefs.getLong(folderIdKey, 0);
-                final FolderInfo workFolder = mModel.findFolderById(folderId);
 
-                if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
-                    // Could not get a work folder. Add all the icons to homescreen.
-                    homescreenApps.addAll(0, workFolderApps);
-                    return;
-                }
-                saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps);
+            for (ShortcutInfo info : pendingShortcuts) {
+                info.rank = startingRank++;
+                writer.addItemToDatabase(info, folderInfo.id, 0, 0, 0);
+            }
 
+            if (folderAlreadyCreated) {
                 // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
                 new MainThreadExecutor().execute(new Runnable() {
 
                     @Override
                     public void run() {
-                        workFolder.prepareAutoUpdate();
-                        for (ShortcutInfo info : workFolderApps) {
-                            workFolder.add(info, false);
+                        folderInfo.prepareAutoUpdate();
+                        for (ShortcutInfo info : pendingShortcuts) {
+                            folderInfo.add(info, false);
                         }
                     }
                 });
             } else {
-                // Create a new folder.
-                final FolderInfo workFolder = new FolderInfo();
-                workFolder.title = mContext.getText(R.string.work_folder_name);
-                workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
-
-                // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
-                for (ShortcutInfo info : workFolderApps) {
-                    workFolder.add(info, false);
-                }
-
-                // Add the item to home screen and DB. This also generates an item id synchronously.
-                ArrayList<ItemInfo> itemList = new ArrayList<>(1);
-                itemList.add(workFolder);
-                mModel.addAndBindAddedWorkspaceItems(itemList);
-                mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();
-
-                saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
+                prefs.edit().putLong(folderIdKey, folderInfo.id).apply();
             }
         }
-
-        @Override
-        public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
-                UserHandle user) {
-            // Do nothing
-        }
-    }
-
-    /**
-     * Add work folder shortcuts to the DB.
-     */
-    private void saveWorkFolderShortcuts(
-            long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
-        for (ItemInfo info : workFolderApps) {
-            info.rank = startingRank++;
-            mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0);
-        }
     }
 
     /**
@@ -225,14 +209,12 @@
      */
     public static void processAllUsers(List<UserHandle> users, Context context) {
         UserManagerCompat userManager = UserManagerCompat.getInstance(context);
-        HashSet<String> validKeys = new HashSet<String>();
+        HashSet<String> validKeys = new HashSet<>();
         for (UserHandle user : users) {
-            addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys);
+            validKeys.add(USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user));
         }
 
-        SharedPreferences prefs = context.getSharedPreferences(
-                LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
-                Context.MODE_PRIVATE);
+        SharedPreferences prefs = prefs(context);
         SharedPreferences.Editor editor = prefs.edit();
         for (String key : prefs.getAll().keySet()) {
             if (!validKeys.contains(key)) {
@@ -242,11 +224,6 @@
         editor.apply();
     }
 
-    private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) {
-        keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial);
-        keysOut.add(USER_FOLDER_ID_PREFIX + userSerial);
-    }
-
     /**
      * For each user, if a work folder has not been created, mark it such that the folder will
      * never get created.
@@ -260,11 +237,8 @@
             if (myUser.equals(user)) {
                 continue;
             }
-
             if (prefs == null) {
-                prefs = context.getSharedPreferences(
-                        LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
-                        Context.MODE_PRIVATE);
+                prefs = prefs(context);
             }
             String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user);
             if (!prefs.contains(folderIdKey)) {
@@ -272,4 +246,9 @@
             }
         }
     }
+
+    public static SharedPreferences prefs(Context context) {
+        return context.getSharedPreferences(
+                LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE);
+    }
 }
diff --git a/src/com/android/launcher3/util/MultiStateAlphaController.java b/src/com/android/launcher3/util/MultiStateAlphaController.java
deleted file mode 100644
index 956fc9e..0000000
--- a/src/com/android/launcher3/util/MultiStateAlphaController.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import java.util.Arrays;
-
-/**
- * A utility class which divides the alpha for a view across multiple states.
- */
-public class MultiStateAlphaController {
-
-    private final View mTargetView;
-    private final float[] mAlphas;
-    private final AccessibilityManager mAm;
-    private int mZeroAlphaListenerCount = 0;
-
-    public MultiStateAlphaController(View view, int stateCount) {
-        mTargetView = view;
-        mAlphas = new float[stateCount];
-        Arrays.fill(mAlphas, 1);
-
-        mAm = (AccessibilityManager) view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-    }
-
-    public void setAlphaAtIndex(float alpha, int index) {
-        mAlphas[index] = alpha;
-        updateAlpha();
-    }
-
-    private void updateAlpha() {
-        // Only update the alpha if no zero-alpha animation is running.
-        if (mZeroAlphaListenerCount > 0) {
-            return;
-        }
-        float finalAlpha = 1;
-        for (float a : mAlphas) {
-            finalAlpha = finalAlpha * a;
-        }
-        mTargetView.setAlpha(finalAlpha);
-        mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE
-                : (mAm.isEnabled() ? View.GONE : View.INVISIBLE));
-    }
-
-    /**
-     * Returns an animator which changes the alpha at the index {@param index}
-     * to {@param finalAlpha}. Alphas at other index are not affected.
-     */
-    public Animator animateAlphaAtIndex(float finalAlpha, final int index) {
-        final ValueAnimator anim;
-
-        if (Float.compare(finalAlpha, mAlphas[index]) == 0) {
-            // Return a dummy animator to avoid null checks.
-            anim = ValueAnimator.ofFloat(0, 0);
-        } else {
-            ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha);
-            animator.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                    float value = (Float) valueAnimator.getAnimatedValue();
-                    setAlphaAtIndex(value, index);
-                }
-            });
-            anim = animator;
-        }
-
-        if (Float.compare(finalAlpha, 0f) == 0) {
-            // In case when any channel is animating to 0, and the current alpha is also 0, do not
-            // update alpha of the target view while the animation is running.
-            // We special case '0' because if any channel is set to 0, values of other
-            // channels do not matter.
-            anim.addListener(new ZeroAlphaAnimatorListener());
-        }
-        return anim;
-    }
-
-    private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter {
-
-        private boolean mStartedAtZero = false;
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0;
-            if (mStartedAtZero) {
-                mZeroAlphaListenerCount++;
-                mTargetView.setAlpha(0);
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (mStartedAtZero) {
-                mZeroAlphaListenerCount--;
-                updateAlpha();
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index e12b2d4..13034dd 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -30,9 +30,11 @@
 import android.text.TextUtils;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 
+import java.net.URISyntaxException;
 import java.util.List;
 
 /**
@@ -149,4 +151,20 @@
                         .appendQueryParameter("id", packageName)
                         .build());
     }
+
+    /**
+     * Creates a new market search intent.
+     */
+    public static Intent getMarketSearchIntent(Context context, String query) {
+        try {
+            Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
+            if (!TextUtils.isEmpty(query)) {
+                intent.setData(
+                        intent.getData().buildUpon().appendQueryParameter("q", query).build());
+            }
+            return intent;
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
deleted file mode 100644
index a57d69f..0000000
--- a/src/com/android/launcher3/util/PillRevealOutlineProvider.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.graphics.Rect;
-import android.view.ViewOutlineProvider;
-
-/**
- * A {@link ViewOutlineProvider} that animates a reveal in a "pill" shape.
- * A pill is simply a round rect, but we assume the width is greater than
- * the height and that the radius is equal to half the height.
- */
-public class PillRevealOutlineProvider extends RevealOutlineAnimation {
-
-    private int mCenterX;
-    private int mCenterY;
-    private float mFinalRadius;
-    protected Rect mPillRect;
-
-    /**
-     * @param x reveal center x
-     * @param y reveal center y
-     * @param pillRect round rect that represents the final pill shape
-     */
-    public PillRevealOutlineProvider(int x, int y, Rect pillRect) {
-        this(x, y, pillRect, pillRect.height() / 2f);
-    }
-
-    public PillRevealOutlineProvider(int x, int y, Rect pillRect, float radius) {
-        mCenterX = x;
-        mCenterY = y;
-        mPillRect = pillRect;
-        mOutlineRadius = mFinalRadius = radius;
-    }
-
-    @Override
-    public boolean shouldRemoveElevationDuringAnimation() {
-        return false;
-    }
-
-    @Override
-    public void setProgress(float progress) {
-        // Assumes width is greater than height.
-        int centerToEdge = Math.max(mCenterX, mPillRect.width() - mCenterX);
-        int currentSize = (int) (progress * centerToEdge);
-
-        // Bound the outline to the final pill shape defined by mPillRect.
-        mOutline.left = Math.max(mPillRect.left, mCenterX - currentSize);
-        mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
-        mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
-        mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
-        mOutlineRadius = Math.min(mFinalRadius, mOutline.height() / 2);
-    }
-}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index 89353e1..7ab0d31 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -19,7 +19,7 @@
 import android.os.Looper;
 
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * A set of utility methods for thread verification.
@@ -27,25 +27,25 @@
 public class Preconditions {
 
     public static void assertNotNull(Object o) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && o == null) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertWorkerThread() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertUIThread() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertNonUiThread() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
             throw new IllegalStateException();
         }
     }
diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java
new file mode 100644
index 0000000..030eb09
--- /dev/null
+++ b/src/com/android/launcher3/util/RunnableWithId.java
@@ -0,0 +1,36 @@
+/*
+ * 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 runnable with an id associated which is used for equality check.
+ */
+public abstract class RunnableWithId implements Runnable {
+
+    public static final int RUNNABLE_ID_BIND_APPS = 1;
+    public static final int RUNNABLE_ID_BIND_WIDGETS = 2;
+
+    public final int id;
+
+    public RunnableWithId(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id;
+    }
+}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index ef10f97..9084bfb 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -10,7 +10,7 @@
 import android.util.Log;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
@@ -19,7 +19,7 @@
 public abstract class SQLiteCacheHelper {
     private static final String TAG = "SQLiteCacheHelper";
 
-    private static final boolean NO_ICON_CACHE = ProviderConfig.IS_DOGFOOD_BUILD &&
+    private static final boolean NO_ICON_CACHE = FeatureFlags.IS_DOGFOOD_BUILD &&
             Utilities.isPropertyEnabled(LogConfig.MEMORY_ONLY_ICON_CACHE);
 
     private final String mTableName;
diff --git a/src/com/android/launcher3/util/SettingsObserver.java b/src/com/android/launcher3/util/SettingsObserver.java
new file mode 100644
index 0000000..6baa242
--- /dev/null
+++ b/src/com/android/launcher3/util/SettingsObserver.java
@@ -0,0 +1,100 @@
+/*
+ * 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/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
new file mode 100644
index 0000000..edbf05a
--- /dev/null
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -0,0 +1,80 @@
+/*
+ * 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.view.View;
+import android.view.Window;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility class to manage various window flags to control system UI.
+ */
+public class SystemUiController {
+
+    // Various UI states in increasing order of priority
+    public static final int UI_STATE_BASE_WINDOW = 0;
+    public static final int UI_STATE_ALL_APPS = 1;
+    public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
+    public static final int UI_STATE_ROOT_VIEW = 3;
+
+    public static final int FLAG_LIGHT_NAV = 1 << 0;
+    public static final int FLAG_DARK_NAV = 1 << 1;
+    public static final int FLAG_LIGHT_STATUS = 1 << 2;
+    public static final int FLAG_DARK_STATUS = 1 << 3;
+
+    private final Window mWindow;
+    private final int[] mStates = new int[4];
+
+    public SystemUiController(Window window) {
+        mWindow = window;
+    }
+
+    public void updateUiState(int uiState, boolean isLight) {
+        updateUiState(uiState, isLight
+                ? (FLAG_LIGHT_NAV | FLAG_LIGHT_STATUS) : (FLAG_DARK_NAV | FLAG_DARK_STATUS));
+    }
+
+    public void updateUiState(int uiState, int flags) {
+        if (mStates[uiState] == flags) {
+            return;
+        }
+        mStates[uiState] = flags;
+
+        int oldFlags = mWindow.getDecorView().getSystemUiVisibility();
+        // Apply the state flags in priority order
+        int newFlags = oldFlags;
+        for (int stateFlag : mStates) {
+            if (Utilities.ATLEAST_OREO) {
+                if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+                    newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+                } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+                    newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+                }
+            }
+
+            if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
+                newFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+            } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
+                newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+            }
+        }
+        if (newFlags != oldFlags) {
+            mWindow.getDecorView().setSystemUiVisibility(newFlags);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java
index 665c371..a7cc42b 100644
--- a/src/com/android/launcher3/util/TestingUtils.java
+++ b/src/com/android/launcher3/util/TestingUtils.java
@@ -3,7 +3,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -11,7 +10,6 @@
 
 import com.android.launcher3.CustomAppWidget;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index d863339..89597b9 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -20,7 +20,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.ColorMatrix;
-import android.view.ContextThemeWrapper;
+import android.graphics.drawable.Drawable;
 
 /**
  * Various utility methods associated with theming.
@@ -31,10 +31,6 @@
         return getAttrColor(context, android.R.attr.colorAccent);
     }
 
-    public static int getColorPrimary(Context context, int theme) {
-        return getAttrColor(new ContextThemeWrapper(context, theme), android.R.attr.colorPrimary);
-    }
-
     public static int getAttrColor(Context context, int attr) {
         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
         int colorAccent = ta.getColor(0, 0);
@@ -42,6 +38,20 @@
         return colorAccent;
     }
 
+    public static boolean getAttrBoolean(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        boolean value = ta.getBoolean(0, false);
+        ta.recycle();
+        return value;
+    }
+
+    public static Drawable getAttrDrawable(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        Drawable value = ta.getDrawable(0);
+        ta.recycle();
+        return value;
+    }
+
     /**
      * Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
      */
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 9bd2882..e5c1dd1 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,13 +16,13 @@
 
 package com.android.launcher3.util;
 
-import android.util.Log;
+import android.os.Process;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewTreeObserver.OnDrawListener;
 
-import com.android.launcher3.DeferredHandler;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
 
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
@@ -34,17 +34,18 @@
         OnAttachStateChangeListener {
 
     private final ArrayList<Runnable> mTasks = new ArrayList<>();
-    private final DeferredHandler mHandler;
+    private final Executor mExecutor;
 
     private Launcher mLauncher;
     private View mAttachedView;
     private boolean mCompleted;
+    private boolean mIsExecuting;
 
     private boolean mLoadAnimationCompleted;
     private boolean mFirstDrawCompleted;
 
-    public ViewOnDrawExecutor(DeferredHandler handler) {
-        mHandler = handler;
+    public ViewOnDrawExecutor(Executor executor) {
+        mExecutor = executor;
     }
 
     public void attachTo(Launcher launcher) {
@@ -64,6 +65,7 @@
     @Override
     public void execute(Runnable command) {
         mTasks.add(command);
+        LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
     }
 
     @Override
@@ -80,6 +82,13 @@
         mAttachedView.post(this);
     }
 
+    /**
+     * Returns whether the executor is still queuing tasks and hasn't yet executed them.
+     */
+    public boolean canQueue() {
+        return !mIsExecuting && !mCompleted;
+    }
+
     public void onLoadAnimationCompleted() {
         mLoadAnimationCompleted = true;
         if (mAttachedView != null) {
@@ -91,8 +100,9 @@
     public void run() {
         // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
         if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
+            mIsExecuting = true;
             for (final Runnable r : mTasks) {
-                mHandler.post(r);
+                mExecutor.execute(r);
             }
             markCompleted();
         }
@@ -101,6 +111,7 @@
     public void markCompleted() {
         mTasks.clear();
         mCompleted = true;
+        mIsExecuting = false;
         if (mAttachedView != null) {
             mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
             mAttachedView.removeOnAttachStateChangeListener(this);
@@ -108,5 +119,6 @@
         if (mLauncher != null) {
             mLauncher.clearPendingExecutor(this);
         }
+        LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT);
     }
 }
diff --git a/src/com/android/launcher3/views/ButtonPreference.java b/src/com/android/launcher3/views/ButtonPreference.java
new file mode 100644
index 0000000..fdcf2ca
--- /dev/null
+++ b/src/com/android/launcher3/views/ButtonPreference.java
@@ -0,0 +1,65 @@
+/*
+ * 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/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
new file mode 100644
index 0000000..c8203f7
--- /dev/null
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -0,0 +1,121 @@
+/*
+ * 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.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+
+/**
+ * Extension of {@link BubbleTextView} which draws two shadows on the text (ambient and key shadows}
+ */
+public class DoubleShadowBubbleTextView extends BubbleTextView {
+
+    private final ShadowInfo mShadowInfo;
+
+    public DoubleShadowBubbleTextView(Context context) {
+        this(context, null);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mShadowInfo = new ShadowInfo(context, attrs, defStyle);
+        setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        // If text is transparent or shadow alpha is 0, don't draw any shadow
+        if (mShadowInfo.skipDoubleShadow(this)) {
+            super.onDraw(canvas);
+            return;
+        }
+        int alpha = Color.alpha(getCurrentTextColor());
+
+        // We enhance the shadow by drawing the shadow twice
+        getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0,
+                ColorUtils.setAlphaComponent(mShadowInfo.ambientShadowColor, alpha));
+
+        drawWithoutBadge(canvas);
+        canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+                getScrollX() + getWidth(),
+                getScrollY() + getHeight(), Region.Op.INTERSECT);
+
+        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
+                ColorUtils.setAlphaComponent(mShadowInfo.keyShadowColor, alpha));
+        drawWithoutBadge(canvas);
+        canvas.restore();
+
+        drawBadgeIfNecessary(canvas);
+    }
+
+    public static class ShadowInfo {
+        public final float ambientShadowBlur;
+        public final int ambientShadowColor;
+
+        public final float keyShadowBlur;
+        public final float keyShadowOffset;
+        public final int keyShadowColor;
+
+        public ShadowInfo(Context c, AttributeSet attrs, int defStyle) {
+
+            TypedArray a = c.obtainStyledAttributes(
+                    attrs, R.styleable.ShadowInfo, defStyle, 0);
+
+            ambientShadowBlur = a.getDimension(R.styleable.ShadowInfo_ambientShadowBlur, 0);
+            ambientShadowColor = a.getColor(R.styleable.ShadowInfo_ambientShadowColor, 0);
+
+            keyShadowBlur = a.getDimension(R.styleable.ShadowInfo_keyShadowBlur, 0);
+            keyShadowOffset = a.getDimension(R.styleable.ShadowInfo_keyShadowOffset, 0);
+            keyShadowColor = a.getColor(R.styleable.ShadowInfo_keyShadowColor, 0);
+            a.recycle();
+        }
+
+        public boolean skipDoubleShadow(TextView textView) {
+            int textAlpha = Color.alpha(textView.getCurrentTextColor());
+            int keyShadowAlpha = Color.alpha(keyShadowColor);
+            int ambientShadowAlpha = Color.alpha(ambientShadowColor);
+            if (textAlpha == 0 || (keyShadowAlpha == 0 && ambientShadowAlpha == 0)) {
+                textView.getPaint().clearShadowLayer();
+                return true;
+            } else if (ambientShadowAlpha > 0) {
+                textView.getPaint().setShadowLayer(ambientShadowBlur, 0, 0,
+                        ColorUtils.setAlphaComponent(ambientShadowColor, textAlpha));
+                return true;
+            } else if (keyShadowAlpha > 0) {
+                textView.getPaint().setShadowLayer(keyShadowBlur, 0.0f, keyShadowOffset,
+                        ColorUtils.setAlphaComponent(keyShadowColor, textAlpha));
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
new file mode 100644
index 0000000..7b5bcdb
--- /dev/null
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -0,0 +1,356 @@
+/*
+ * 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.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+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;
+
+/**
+ * The track and scrollbar that shows when you scroll the list.
+ */
+public class RecyclerViewFastScroller extends View {
+
+    private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+
+    private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
+            new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
+
+                @Override
+                public Integer get(RecyclerViewFastScroller scrollBar) {
+                    return scrollBar.mWidth;
+                }
+
+                @Override
+                public void set(RecyclerViewFastScroller scrollBar, Integer value) {
+                    scrollBar.setTrackWidth(value);
+                }
+            };
+
+    private final static int MAX_TRACK_ALPHA = 30;
+    private final static int SCROLL_BAR_VIS_DURATION = 150;
+    private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
+
+    private final int mMinWidth;
+    private final int mMaxWidth;
+    private final int mThumbPadding;
+
+    /** Keeps the last known scrolling delta/velocity along y-axis. */
+    private int mDy = 0;
+    private final float mDeltaThreshold;
+
+    private final ViewConfiguration mConfig;
+
+    // Current width of the track
+    private int mWidth;
+    private ObjectAnimator mWidthAnimator;
+
+    private final Paint mThumbPaint;
+    protected final int mThumbHeight;
+
+    private final Paint mTrackPaint;
+
+    private float mLastTouchY;
+    private boolean mIsDragging;
+    private boolean mIsThumbDetached;
+    private final boolean mCanThumbDetach;
+    private boolean mIgnoreDragGesture;
+
+    // This is the offset from the top of the scrollbar when the user first starts touching.  To
+    // prevent jumping, this offset is applied as the user scrolls.
+    protected int mTouchOffsetY;
+    protected int mThumbOffsetY;
+
+    // Fast scroller popup
+    private TextView mPopupView;
+    private boolean mPopupVisible;
+    private String mPopupSectionName;
+
+    protected BaseRecyclerView mRv;
+
+    private int mDownX;
+    private int mDownY;
+    private int mLastY;
+
+    public RecyclerViewFastScroller(Context context) {
+        this(context, null);
+    }
+
+    public RecyclerViewFastScroller(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mTrackPaint = new Paint();
+        mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+        mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
+
+        mThumbPaint = new Paint();
+        mThumbPaint.setAntiAlias(true);
+        mThumbPaint.setColor(Themes.getColorAccent(context));
+        mThumbPaint.setStyle(Paint.Style.FILL);
+
+        Resources res = getResources();
+        mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
+        mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
+
+        mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
+        mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
+
+        mConfig = ViewConfiguration.get(context);
+        mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
+
+        TypedArray ta =
+                context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
+        mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false);
+        ta.recycle();
+    }
+
+    public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
+        mRv = rv;
+        mRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+                mDy = dy;
+
+                // TODO(winsonc): If we want to animate the section heads while scrolling, we can
+                //                initiate that here if the recycler view scroll state is not
+                //                RecyclerView.SCROLL_STATE_IDLE.
+
+                mRv.onUpdateScrollbar(dy);
+            }
+        });
+
+        mPopupView = popupView;
+        mPopupView.setBackground(
+                new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources())));
+    }
+
+    public void reattachThumbToScroll() {
+        mIsThumbDetached = false;
+    }
+
+    public void setThumbOffsetY(int y) {
+        if (mThumbOffsetY == y) {
+            return;
+        }
+        mThumbOffsetY = y;
+        invalidate();
+    }
+
+    public int getThumbOffsetY() {
+        return mThumbOffsetY;
+    }
+
+    private void setTrackWidth(int width) {
+        if (mWidth == width) {
+            return;
+        }
+        mWidth = width;
+        invalidate();
+    }
+
+    public int getThumbHeight() {
+        return mThumbHeight;
+    }
+
+    public boolean isDraggingThumb() {
+        return mIsDragging;
+    }
+
+    public boolean isThumbDetached() {
+        return mIsThumbDetached;
+    }
+
+    /**
+     * Handles the touch event and determines whether to show the fast scroller (or updates it if
+     * it is already showing).
+     */
+    public boolean handleTouchEvent(MotionEvent ev) {
+        int x = (int) ev.getX();
+        int y = (int) ev.getY();
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                // Keep track of the down positions
+                mDownX = x;
+                mDownY = mLastY = y;
+
+                if ((Math.abs(mDy) < mDeltaThreshold &&
+                        mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+                    // now the touch events are being passed to the {@link WidgetCell} until the
+                    // touch sequence goes over the touch slop.
+                    mRv.stopScroll();
+                }
+                if (isNearThumb(x, y)) {
+                    mTouchOffsetY = mDownY - mThumbOffsetY;
+                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
+                        && mRv.supportsFastScrolling()
+                        && isNearScrollBar(mDownX)) {
+                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mLastY = y;
+
+                // Check if we should start scrolling, but ignore this fastscroll gesture if we have
+                // exceeded some fixed movement
+                mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
+                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
+                        isNearThumb(mDownX, mLastY) &&
+                        Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
+                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                }
+                if (mIsDragging) {
+                    updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mRv.onFastScrollCompleted();
+                mTouchOffsetY = 0;
+                mLastTouchY = 0;
+                mIgnoreDragGesture = false;
+                if (mIsDragging) {
+                    mIsDragging = false;
+                    animatePopupVisibility(false);
+                    showActiveScrollbar(false);
+                }
+                break;
+        }
+        return mIsDragging;
+    }
+
+    private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+        mRv.getParent().requestDisallowInterceptTouchEvent(true);
+        mIsDragging = true;
+        if (mCanThumbDetach) {
+            mIsThumbDetached = true;
+        }
+        mTouchOffsetY += (lastY - downY);
+        animatePopupVisibility(true);
+        showActiveScrollbar(true);
+    }
+
+    private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+        // Update the fastscroller section name at this touch position
+        int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
+        float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
+        String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
+        if (!sectionName.equals(mPopupSectionName)) {
+            mPopupSectionName = sectionName;
+            mPopupView.setText(sectionName);
+        }
+        animatePopupVisibility(!sectionName.isEmpty());
+        updatePopupY(lastY);
+        mLastTouchY = boundedY;
+        setThumbOffsetY((int) mLastTouchY);
+    }
+
+    public void onDraw(Canvas canvas) {
+        if (mThumbOffsetY < 0) {
+            return;
+        }
+        int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(getWidth() / 2, mRv.getPaddingTop());
+        // Draw the track
+        float halfW = mWidth / 2;
+        canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
+                mWidth, mWidth, mTrackPaint);
+
+        canvas.translate(0, mThumbOffsetY);
+        halfW += mThumbPadding;
+        float r = mWidth + mThumbPadding + mThumbPadding;
+        canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
+        canvas.restoreToCount(saveCount);
+    }
+
+
+    /**
+     * Animates the width of the scrollbar.
+     */
+    private void showActiveScrollbar(boolean isScrolling) {
+        if (mWidthAnimator != null) {
+            mWidthAnimator.cancel();
+        }
+
+        mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
+                isScrolling ? mMaxWidth : mMinWidth);
+        mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
+        mWidthAnimator.start();
+    }
+
+    /**
+     * Returns whether the specified point is inside the thumb bounds.
+     */
+    private boolean isNearThumb(int x, int y) {
+        int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
+
+        return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
+    }
+
+    /**
+     * Returns true if AllAppsTransitionController can handle vertical motion
+     * beginning at this point.
+     */
+    public boolean shouldBlockIntercept(int x, int y) {
+        return isNearThumb(x, y);
+    }
+
+    /**
+     * Returns whether the specified x position is near the scroll bar.
+     */
+    public boolean isNearScrollBar(int x) {
+        return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
+    }
+
+    private void animatePopupVisibility(boolean visible) {
+        if (mPopupVisible != visible) {
+            mPopupVisible = visible;
+            mPopupView.animate().cancel();
+            mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
+        }
+    }
+
+    private void updatePopupY(int lastTouchY) {
+        int height = mPopupView.getHeight();
+        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
+                + mRv.getPaddingTop();
+        top = Utilities.boundToRange(top,
+                mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
+        mPopupView.setTranslationY(top);
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index e8f13a1..62b6903 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.widget;
 
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 6628971..19be28d 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -116,7 +116,7 @@
         } else {
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
             Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
-            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, Build.VERSION_CODES.O);
+            preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0);
             mAddInfo.spanX = mAddInfo.spanY = 1;
             scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
 
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index 629f30c..5387be8 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -15,10 +15,8 @@
  */
 package com.android.launcher3.widget;
 
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,7 +24,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.util.PendingRequestArgs;
 
 /**
@@ -56,15 +53,8 @@
 
     public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
         launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
-
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mProviderInfo.provider);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
-                mProviderInfo.getProfile());
-        // TODO: we need to make sure that this accounts for the options bundle.
-        // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
-        launcher.startActivityForResult(intent, requestCode);
+        launcher.getAppWidgetHost()
+                .startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
     }
 
     /**
@@ -85,9 +75,7 @@
             return false;
         }
         launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
-
-        AppWidgetManagerCompat.getInstance(launcher).startConfigActivity(
-                mProviderInfo, appWidgetId, launcher, launcher.getAppWidgetHost(), requestCode);
+        launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
         return true;
     }
 
diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java
index 3e89eeb..335b8c7 100644
--- a/src/com/android/launcher3/widget/WidgetListRowEntry.java
+++ b/src/com/android/launcher3/widget/WidgetListRowEntry.java
@@ -41,4 +41,8 @@
         this.widgets = items;
     }
 
+    @Override
+    public String toString() {
+        return pkgItem.packageName + ":" + widgets.size();
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 5fe00c2..01101ac 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -21,14 +21,13 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
@@ -41,13 +40,16 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.VerticalPullDetector;
+import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.GradientView;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TouchController;
 
 import java.util.List;
@@ -56,7 +58,7 @@
  * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
  */
 public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController,
-        VerticalPullDetector.Listener, View.OnClickListener, View.OnLongClickListener,
+        SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener,
         DragController.DragListener {
 
     private int mTranslationYOpen;
@@ -67,25 +69,27 @@
     private ItemInfo mOriginalItemInfo;
     private ObjectAnimator mOpenCloseAnimator;
     private Interpolator mFastOutSlowInInterpolator;
-    private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
+    private SwipeDetector.ScrollInterpolator mScrollInterpolator;
     private Rect mInsets;
-    private boolean mWasNavBarLight;
-    private VerticalPullDetector mVerticalPullDetector;
+    private SwipeDetector mSwipeDetector;
+    private GradientView mGradientBackground;
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme), attrs, defStyleAttr);
+        super(context, attrs, defStyleAttr);
         setWillNotDraw(false);
         mLauncher = Launcher.getLauncher(context);
         mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
-        mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
-        mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
+        mFastOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+        mScrollInterpolator = new SwipeDetector.ScrollInterpolator();
         mInsets = new Rect();
-        mVerticalPullDetector = new VerticalPullDetector(context);
-        mVerticalPullDetector.setListener(this);
+        mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
+        mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate(
+                R.layout.gradient_bg, mLauncher.getDragLayer(), false);
     }
 
     @Override
@@ -103,8 +107,8 @@
 
         onWidgetsBound();
 
-        mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
-                & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
+        mLauncher.getDragLayer().addView(mGradientBackground);
+        mGradientBackground.setVisibility(VISIBLE);
         mLauncher.getDragLayer().addView(this);
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         setTranslationY(mTranslationYClosed);
@@ -180,14 +184,17 @@
             return;
         }
         mIsOpen = true;
-        setLightNavBar(true);
+        boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+        mLauncher.getSystemUiController().updateUiState(
+                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+                isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
         if (animate) {
             mOpenCloseAnimator.setValues(new PropertyListBuilder()
                     .translationY(mTranslationYOpen).build());
             mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    mVerticalPullDetector.finishedScrolling();
+                    mSwipeDetector.finishedScrolling();
                 }
             });
             mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator);
@@ -208,24 +215,25 @@
             mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    mIsOpen = false;
-                    mVerticalPullDetector.finishedScrolling();
-                    ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this);
-                    setLightNavBar(mWasNavBarLight);
+                    mSwipeDetector.finishedScrolling();
+                    onCloseComplete();
                 }
             });
-            mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState()
+            mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState()
                     ? mFastOutSlowInInterpolator : mScrollInterpolator);
             mOpenCloseAnimator.start();
         } else {
             setTranslationY(mTranslationYClosed);
-            setLightNavBar(mWasNavBarLight);
-            mIsOpen = false;
+            onCloseComplete();
         }
     }
 
-    private void setLightNavBar(boolean lightNavBar) {
-        mLauncher.activateLightSystemBars(lightNavBar, false /* statusBar */, true /* navBar */);
+    private void onCloseComplete() {
+        mIsOpen = false;
+        mLauncher.getDragLayer().removeView(mGradientBackground);
+        mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this);
+        mLauncher.getSystemUiController().updateUiState(
+                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
     }
 
     @Override
@@ -256,7 +264,7 @@
                 getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
     }
 
-    /* VerticalPullDetector.Listener */
+    /* SwipeDetector.Listener */
 
     @Override
     public void onDragStart(boolean start) {
@@ -270,15 +278,24 @@
     }
 
     @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
+        if (mGradientBackground == null) return;
+        float p = (mTranslationYClosed - translationY) / mTranslationYRange;
+        boolean showScrim = p <= 0;
+        mGradientBackground.setProgress(p, showScrim);
+    }
+
+    @Override
     public void onDragEnd(float velocity, boolean fling) {
         if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
             mScrollInterpolator.setVelocityAtZero(velocity);
-            mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+            mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
                     (mTranslationYClosed - getTranslationY()) / mTranslationYRange));
             close(true);
         } else {
             mIsOpen = false;
-            mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+            mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity,
                     (getTranslationY() - mTranslationYOpen) / mTranslationYRange));
             open(true);
         }
@@ -286,17 +303,17 @@
 
     @Override
     public boolean onControllerTouchEvent(MotionEvent ev) {
-        return mVerticalPullDetector.onTouchEvent(ev);
+        return mSwipeDetector.onTouchEvent(ev);
     }
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        int directionsToDetectScroll = mVerticalPullDetector.isIdleState() ?
-                VerticalPullDetector.DIRECTION_DOWN : 0;
-        mVerticalPullDetector.setDetectableScrollConditions(
+        int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
+                SwipeDetector.DIRECTION_NEGATIVE : 0;
+        mSwipeDetector.setDetectableScrollConditions(
                 directionsToDetectScroll, false);
-        mVerticalPullDetector.onTouchEvent(ev);
-        return mVerticalPullDetector.isDraggingOrSettling();
+        mSwipeDetector.onTouchEvent(ev);
+        return mSwipeDetector.isDraggingOrSettling();
     }
 
     /* DragListener */
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 4e296bf..acec3dd 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -17,10 +17,12 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.graphics.Point;
 import android.support.v7.widget.LinearLayoutManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Toast;
@@ -31,8 +33,10 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.model.PackageItemInfo;
@@ -74,7 +78,11 @@
     public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mLauncher = Launcher.getLauncher(context);
-        mAdapter = new WidgetsListAdapter(this, this, context);
+        LauncherAppState apps = LauncherAppState.getInstance(context);
+        mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context),
+                apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this,
+                new WidgetsDiffReporter(apps.getIconCache()));
+        mAdapter.setNotifyListener();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
         }
@@ -139,8 +147,6 @@
         if (LOGD) {
             Log.d(TAG, String.format("onLongClick [v=%s]", v));
         }
-        // Return early if this is not initiated from a touch
-        if (!v.isInTouchMode()) return false;
         // When we  are in transition, disregard long clicks
         if (mLauncher.getWorkspace().isSwitchingState()) return false;
         // Return if global dragging is not enabled
@@ -234,7 +240,6 @@
      */
     public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) {
         mAdapter.setWidgets(model);
-        mAdapter.notifyDataSetChanged();
 
         View loader = getContentView().findViewById(R.id.loader);
         if (loader != null) {
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
new file mode 100644
index 0000000..52deec3
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.util.Log;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly.
+ */
+public class WidgetsDiffReporter {
+    private final boolean DEBUG = false;
+    private final String TAG = "WidgetsDiffReporter";
+    private final IconCache mIconCache;
+    private NotifyListener mListener;
+
+    public interface NotifyListener {
+        void notifyDataSetChanged();
+        void notifyItemChanged(int index);
+        void notifyItemInserted(int index);
+        void notifyItemRemoved(int index);
+    }
+
+    public WidgetsDiffReporter(IconCache iconCache) {
+        mIconCache = iconCache;
+    }
+
+    public void setListener(NotifyListener listener) {
+        mListener = listener;
+    }
+
+    public void process(ArrayList<WidgetListRowEntry> currentEntries,
+            ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
+        if (DEBUG) {
+            Log.d(TAG, "process oldEntries#=" + currentEntries.size()
+                    + " newEntries#=" + newEntries.size());
+        }
+        if (currentEntries.size() == 0 && newEntries.size() > 0) {
+            currentEntries.addAll(newEntries);
+            mListener.notifyDataSetChanged();
+            return;
+        }
+        ArrayList<WidgetListRowEntry> orgEntries =
+                (ArrayList<WidgetListRowEntry>) currentEntries.clone();
+        Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
+        Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
+
+        WidgetListRowEntry orgRowEntry = orgIter.next();
+        WidgetListRowEntry newRowEntry = newIter.next();
+
+        do {
+            int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
+            if (DEBUG) {
+                Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
+                        diff, orgRowEntry != null? orgRowEntry.toString() : null,
+                        newRowEntry != null? newRowEntry.toString() : null));
+            }
+            int index = -1;
+            if (diff < 0) {
+                index = currentEntries.indexOf(orgRowEntry);
+                mListener.notifyItemRemoved(index);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
+                            orgRowEntry.titleSectionName));
+                }
+                currentEntries.remove(index);
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+            } else if (diff > 0) {
+                index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
+                        currentEntries.size();
+                currentEntries.add(index, newRowEntry);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
+                            newRowEntry.titleSectionName));
+                }
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+                mListener.notifyItemInserted(index);
+
+            } else {
+                // same package name but,
+                // did the icon, title, etc, change?
+                // or did the widget size and desc, span, etc change?
+                if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
+                        !orgRowEntry.widgets.equals(newRowEntry.widgets)) {
+                    index = currentEntries.indexOf(orgRowEntry);
+                    currentEntries.set(index, newRowEntry);
+                    mListener.notifyItemChanged(index);
+                    if (DEBUG) {
+                        Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
+                                newRowEntry.titleSectionName));
+                    }
+                }
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+            }
+        } while(orgRowEntry != null || newRowEntry != null);
+    }
+
+    /**
+     * Compare package name using the same comparator as in {@link WidgetsListAdapter}.
+     * Also handle null row pointers.
+     */
+    private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
+            WidgetListRowEntryComparator comparator) {
+        if (curRow == null && newRow == null) {
+            throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
+        }
+
+        if (curRow == null && newRow != null) {
+            return 1; // new row needs to be inserted
+        } else if (curRow != null && newRow == null) {
+            return -1; // old row needs to be deleted
+        }
+        return comparator.compare(curRow, newRow);
+    }
+
+    private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
+        return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
+                !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index a1eb0ab..6b1800c 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -21,9 +21,10 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
@@ -55,40 +56,67 @@
 
     private final WidgetPreviewLoader mWidgetPreviewLoader;
     private final LayoutInflater mLayoutInflater;
-
-    private final View.OnClickListener mIconClickListener;
-    private final View.OnLongClickListener mIconLongClickListener;
-
-    private final ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
     private final AlphabeticIndexCompat mIndexer;
 
+    private final OnClickListener mIconClickListener;
+    private final OnLongClickListener mIconLongClickListener;
     private final int mIndent;
+    private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
+    private final WidgetsDiffReporter mDiffReporter;
 
-    public WidgetsListAdapter(View.OnClickListener iconClickListener,
-            View.OnLongClickListener iconLongClickListener,
-            Context context) {
-        mLayoutInflater = LayoutInflater.from(context);
-        mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache();
-
-        mIndexer = new AlphabeticIndexCompat(context);
-
+    public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
+            WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat,
+            OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
+            WidgetsDiffReporter diffReporter) {
+        mLayoutInflater = layoutInflater;
+        mWidgetPreviewLoader = widgetPreviewLoader;
+        mIndexer = indexCompat;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
+        mDiffReporter = diffReporter;
     }
 
-    public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
-        mEntries.clear();
-        WidgetItemComparator widgetComparator = new WidgetItemComparator();
+    public void setNotifyListener() {
+        mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() {
+            @Override
+            public void notifyDataSetChanged() {
+                WidgetsListAdapter.this.notifyDataSetChanged();
+            }
 
+            @Override
+            public void notifyItemChanged(int index) {
+                WidgetsListAdapter.this.notifyItemChanged(index);
+            }
+
+            @Override
+            public void notifyItemInserted(int index) {
+                WidgetsListAdapter.this.notifyItemInserted(index);
+            }
+
+            @Override
+            public void notifyItemRemoved(int index) {
+                WidgetsListAdapter.this.notifyItemRemoved(index);
+            }
+        });
+    }
+
+    /**
+     * Update the widget list.
+     */
+    public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
+        ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>();
+
+        WidgetItemComparator widgetComparator = new WidgetItemComparator();
         for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) {
             WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
             row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title);
             Collections.sort(row.widgets, widgetComparator);
-            mEntries.add(row);
+            tempEntries.add(row);
         }
-
-        Collections.sort(mEntries, new WidgetListRowEntryComparator());
+        WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
+        Collections.sort(tempEntries, rowComparator);
+        mDiffReporter.process(mEntries, tempEntries, rowComparator);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index e0a80c6..9730a82 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -21,9 +21,8 @@
 import android.support.v7.widget.LinearLayoutManager;
 import android.util.AttributeSet;
 import android.view.View;
+
 import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.model.WidgetsModel;
 
 /**
  * The widgets recycler view.
@@ -60,10 +59,6 @@
         setLayoutManager(new LinearLayoutManager(getContext()));
     }
 
-    public int getFastScrollerTrackColor(int defaultTrackColor) {
-        return Color.WHITE;
-    }
-
     @Override
     public void setAdapter(Adapter adapter) {
         super.setAdapter(adapter);
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/BuildConfig.java
similarity index 67%
copy from src_config/com/android/launcher3/config/ProviderConfig.java
copy to src_config/com/android/launcher3/BuildConfig.java
index 491fa65..4df75a1 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/BuildConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.config;
+package com.android.launcher3;
 
-public class ProviderConfig {
-
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
-    public static final boolean IS_DOGFOOD_BUILD = true;
+/**
+ * Config file used by Make. This file is automatically generated when using gradle.
+ */
+public class BuildConfig {
+    public static final String APPLICATION_ID = "com.android.launcher3";
 }
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
deleted file mode 100644
index 4e337a2..0000000
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.config;
-
-/**
- * Defines a set of flags used to control various launcher behaviors
- */
-public final class FeatureFlags {
-    private FeatureFlags() {}
-
-    // Custom flags go below this
-    public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
-    public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
-    public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
-    public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
-    public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
-    public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = false;
-    // When enabled allows to use any point on the fast scrollbar to start dragging.
-    public static boolean LAUNCHER3_DIRECT_SCROLL = true;
-    // When enabled while all-apps open, the soft input will be set to adjust resize .
-    public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
-
-
-    // Feature flag to 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;
-    // When enabled fling down gesture on the first workspace triggers search.
-    public static final boolean PULLDOWN_SEARCH = false;
-    // When enabled the status bar may show dark icons based on the top of the wallpaper.
-    public static final boolean LIGHT_STATUS_BAR = false;
-    // When enabled icons are badged with the number of notifications associated with that app.
-    public static final boolean BADGE_ICONS = true;
-    // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in this class.
-    public static final boolean LEGACY_ICON_TREATMENT = true;
-    // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
-    public static final boolean ADAPTIVE_ICON_SHADOW = true;
-    // When enabled, app discovery will be enabled if service is implemented
-    public static final boolean DISCOVERY_ENABLED = false;
-}
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 72%
rename from src_config/com/android/launcher3/config/ProviderConfig.java
rename to src_flags/com/android/launcher3/config/FeatureFlags.java
index 491fa65..3ffb6c9 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,10 @@
 
 package com.android.launcher3.config;
 
-public class ProviderConfig {
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ */
+public final class FeatureFlags extends BaseFlags {
 
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
-    public static final boolean IS_DOGFOOD_BUILD = true;
+    private FeatureFlags() {}
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index 6e6453c..e8797a7 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -17,11 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
-	android-support-test \
-	ub-uiautomator \
-	legacy-android-test \
-	mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
diff --git a/tests/res/raw/db_schema_v10.json b/tests/res/raw/db_schema_v10.json
new file mode 100644
index 0000000..a5e290e
--- /dev/null
+++ b/tests/res/raw/db_schema_v10.json
@@ -0,0 +1,4 @@
+{
+  "version" : 10,
+  "downgrade_to_9" : []
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
deleted file mode 100644
index 230d623..0000000
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.android.launcher3;
-
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Tests the {@link DeviceProfile} and {@link InvariantDeviceProfile}.
- */
-@SmallTest
-public class InvariantDeviceProfileTest extends AndroidTestCase {
-
-    private static final String TAG = "DeviceProfileTest";
-    private static final boolean DEBUG = false;
-
-    private InvariantDeviceProfile mInvariantProfile;
-    private ArrayList<InvariantDeviceProfile> mPredefinedDeviceProfiles;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mInvariantProfile = new InvariantDeviceProfile(getContext());
-        mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles(getContext());
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        // Nothing to tear down as this class only tests static methods.
-    }
-
-    public void testFindClosestDeviceProfile2() {
-        for (InvariantDeviceProfile idf: mPredefinedDeviceProfiles) {
-            ArrayList<InvariantDeviceProfile> predefinedProfilesCopy =
-                    new ArrayList<>(mPredefinedDeviceProfiles);
-            ArrayList<InvariantDeviceProfile> closestProfiles =
-                    mInvariantProfile.findClosestDeviceProfiles(
-                            idf.minWidthDps, idf.minHeightDps, predefinedProfilesCopy
-                    );
-            assertTrue(closestProfiles.get(0).equals(idf));
-        }
-    }
-
-    /**
-     * Used to print out how the invDistWeightedInterpolate works between device profiles to
-     * tweak the two constants that control how the interpolation curve is shaped.
-     */
-    public void testInvInterpolation() {
-
-        InvariantDeviceProfile p1 = mPredefinedDeviceProfiles.get(7); // e.g., Large Phone
-        InvariantDeviceProfile p2 = mPredefinedDeviceProfiles.get(8); // e.g., Nexus 7
-
-        ArrayList<PointF> pts = createInterpolatedPoints(
-                new PointF(p1.minWidthDps, p1.minHeightDps),
-                new PointF(p2.minWidthDps, p2.minHeightDps),
-                20f);
-
-        for (int i = 0; i < pts.size(); i++) {
-            ArrayList<InvariantDeviceProfile> closestProfiles =
-                    mInvariantProfile.findClosestDeviceProfiles(
-                            pts.get(i).x, pts.get(i).y, mPredefinedDeviceProfiles);
-            InvariantDeviceProfile result =
-                    mInvariantProfile.invDistWeightedInterpolate(
-                            pts.get(i).x, pts.get(i).y, closestProfiles);
-            if (DEBUG) {
-                Log.d(TAG, String.format("width x height = (%f, %f)] iconSize = %f",
-                        pts.get(i).x, pts.get(i).y, result.iconSize));
-            }
-        }
-    }
-
-    private ArrayList<PointF> createInterpolatedPoints(PointF a, PointF b, float numPts) {
-        ArrayList<PointF> result = new ArrayList<PointF>();
-        result.add(a);
-        for (float i = 1; i < numPts; i = i + 1.0f) {
-            result.add(new PointF((b.x * i +  a.x * (numPts - i)) / numPts,
-                    (b.y * i + a.y * (numPts - i)) / numPts));
-        }
-        result.add(b);
-        return result;
-    }
-
-    /**
-     * Ensures that system calls (e.g., WindowManager, DisplayMetrics) that require contexts are
-     * properly working to generate minimum width and height of the display.
-     */
-    public void test_hammerhead() {
-        if (!android.os.Build.DEVICE.equals("hammerhead")) {
-            return;
-        }
-        assertEquals(4, mInvariantProfile.numRows);
-        assertEquals(4, mInvariantProfile.numColumns);
-        assertEquals(5, mInvariantProfile.numHotseatIcons);
-    }
-
-    // Add more tests for other devices, however, running them once on a single device is enough
-    // for verifying that for a platform version, the WindowManager and DisplayMetrics is
-    // working as intended.
-}
diff --git a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
deleted file mode 100644
index e9c2ebc..0000000
--- a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-import android.content.ComponentName;
-import android.test.InstrumentationTestCase;
-
-import com.android.launcher3.AppInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Unit tests for {@link DefaultAppSearchAlgorithm}
- */
-public class DefaultAppSearchAlgorithmTest extends InstrumentationTestCase {
-
-    private List<AppInfo> mAppsList;
-    private DefaultAppSearchAlgorithm mAlgorithm;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mAppsList = new ArrayList<>();
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mAlgorithm = new DefaultAppSearchAlgorithm(mAppsList);
-            }
-        });
-    }
-
-    public void testMatches() {
-        assertTrue(mAlgorithm.matches(getInfo("white cow"), "cow"));
-        assertTrue(mAlgorithm.matches(getInfo("whiteCow"), "cow"));
-        assertTrue(mAlgorithm.matches(getInfo("whiteCOW"), "cow"));
-        assertTrue(mAlgorithm.matches(getInfo("whitecowCOW"), "cow"));
-        assertTrue(mAlgorithm.matches(getInfo("white2cow"), "cow"));
-
-        assertFalse(mAlgorithm.matches(getInfo("whitecow"), "cow"));
-        assertFalse(mAlgorithm.matches(getInfo("whitEcow"), "cow"));
-
-        assertTrue(mAlgorithm.matches(getInfo("whitecowCow"), "cow"));
-        assertTrue(mAlgorithm.matches(getInfo("whitecow cow"), "cow"));
-        assertFalse(mAlgorithm.matches(getInfo("whitecowcow"), "cow"));
-        assertFalse(mAlgorithm.matches(getInfo("whit ecowcow"), "cow"));
-
-        assertTrue(mAlgorithm.matches(getInfo("cats&dogs"), "dog"));
-        assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "dog"));
-        assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "&"));
-
-        assertTrue(mAlgorithm.matches(getInfo("2+43"), "43"));
-        assertFalse(mAlgorithm.matches(getInfo("2+43"), "3"));
-
-        assertTrue(mAlgorithm.matches(getInfo("Q"), "q"));
-        assertTrue(mAlgorithm.matches(getInfo("  Q"), "q"));
-
-        // match lower case words
-        assertTrue(mAlgorithm.matches(getInfo("elephant"), "e"));
-
-        assertTrue(mAlgorithm.matches(getInfo("电子邮件"), "电"));
-        assertTrue(mAlgorithm.matches(getInfo("电子邮件"), "电子"));
-        assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "子"));
-        assertFalse(mAlgorithm.matches(getInfo("电子邮件"), "邮件"));
-    }
-
-    private AppInfo getInfo(String title) {
-        AppInfo info = new AppInfo();
-        info.title = title;
-        info.componentName = new ComponentName("Test", title);
-        return info;
-    }
-}
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
new file mode 100644
index 0000000..846a163
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+import 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;
+
+/**
+ * Unit tests for {@link DefaultAppSearchAlgorithm}
+ */
+@RunWith(AndroidJUnit4.class)
+public class DefaultAppSearchAlgorithmTest {
+    private static final DefaultAppSearchAlgorithm.StringMatcher MATCHER =
+            DefaultAppSearchAlgorithm.StringMatcher.getInstance();
+
+    @Test
+    public void testMatches() {
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white cow"), "cow", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCow"), "cow", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCOW"), "cow", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCOW"), "cow", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white2cow"), "cow", MATCHER));
+
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecow"), "cow", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitEcow"), "cow", MATCHER));
+
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCow"), "cow", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecow cow"), "cow", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecowcow"), "cow", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whit ecowcow"), "cow", MATCHER));
+
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&dogs"), "dog", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "dog", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "&", MATCHER));
+
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "43", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "3", MATCHER));
+
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Q"), "q", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("  Q"), "q", MATCHER));
+
+        // match lower case words
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("elephant"), "e", MATCHER));
+
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电子", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "子", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "邮件", MATCHER));
+
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("Bot"), "ba", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("bot"), "ba", MATCHER));
+    }
+
+    @Test
+    public void testMatchesVN() {
+        if (!Utilities.ATLEAST_NOUGAT) {
+            return;
+        }
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드"), "다", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("드라이브"), "드", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("운로 드라이브"), "ㄷ", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åbç", MATCHER));
+        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Alpha"), "ål", MATCHER));
+
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷㄷ", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("로드라이브"), "ㄷ", MATCHER));
+        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åç", MATCHER));
+    }
+
+    private AppInfo getInfo(String title) {
+        AppInfo info = new AppInfo();
+        info.title = title;
+        info.componentName = new ComponentName("Test", title);
+        return info;
+    }
+}
diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java
index c24cc3f..7048c28 100644
--- a/tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -37,6 +37,9 @@
     }
 
     public void testPrintLog() throws Exception {
+        if (!FileLog.ENABLED) {
+            return;
+        }
         FileLog.print("Testing", "hoolalala");
         StringWriter writer = new StringWriter();
         FileLog.flushAll(new PrintWriter(writer));
@@ -54,6 +57,9 @@
     }
 
     public void testOldFileTruncated() throws Exception {
+        if (!FileLog.ENABLED) {
+            return;
+        }
         FileLog.print("Testing", "hoolalala");
         StringWriter writer = new StringWriter();
         FileLog.flushAll(new PrintWriter(writer));
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index d0ba907..82f34e4 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -10,9 +10,9 @@
 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.config.ProviderConfig;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.Provider;
@@ -20,8 +20,9 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.List;
 
+import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -50,7 +51,11 @@
     }
 
     private AddWorkspaceItemsTask newTask(ItemInfo... items) {
-        return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) {
+        List<Pair<ItemInfo, Object>> list = new ArrayList<>();
+        for (ItemInfo item : items) {
+            list.add(Pair.create(item, null));
+        }
+        return new AddWorkspaceItemsTask(Provider.of(list)) {
 
             @Override
             protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
@@ -126,7 +131,7 @@
         // 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(), any(ArrayList.class));
+                animated.capture());
         assertTrue(notAnimated.getValue().isEmpty());
 
         assertEquals(1, animated.getValue().size());
@@ -178,6 +183,6 @@
             v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
             ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
         }
-        getMockContentResolver().applyBatch(ProviderConfig.AUTHORITY, ops);
+        getMockContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
     }
 }
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index b9944db..3d03507 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -16,15 +16,14 @@
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppFilter;
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.DeferredHandler;
 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.BaseModelUpdateTask;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.TestLauncherProvider;
@@ -36,6 +35,7 @@
 import java.lang.reflect.Field;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.atLeast;
@@ -64,7 +64,7 @@
     public Callbacks callbacks;
 
     public BaseModelUpdateTaskTestCase() {
-        super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+        super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
     }
 
     @Override
@@ -75,8 +75,10 @@
         appState = mock(LauncherAppState.class);
         model = mock(LauncherModel.class);
         modelWriter = mock(ModelWriter.class);
+
         when(appState.getModel()).thenReturn(model);
         when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+        when(model.getCallback()).thenReturn(callbacks);
 
         myUser = Process.myUserHandle();
 
@@ -94,22 +96,15 @@
     /**
      * Synchronously executes the task and returns all the UI callbacks posted.
      */
-    public List<Runnable> executeTaskForTest(BaseModelUpdateTask task) throws Exception {
-        LauncherModel mockModel = mock(LauncherModel.class);
-        when(mockModel.getCallback()).thenReturn(callbacks);
+    public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
+        when(model.isModelLoaded()).thenReturn(true);
 
-        Field f = BaseModelUpdateTask.class.getDeclaredField("mModel");
-        f.setAccessible(true);
-        f.set(task, mockModel);
+        Executor mockExecutor = mock(Executor.class);
 
-        DeferredHandler mockHandler = mock(DeferredHandler.class);
-        f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
-        f.setAccessible(true);
-        f.set(task, mockHandler);
-
-        task.execute(appState, bgDataModel, allAppsList);
+        task.init(appState, model, bgDataModel, allAppsList, mockExecutor);
+        task.run();
         ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        verify(mockHandler, atLeast(0)).post(captor.capture());
+        verify(mockExecutor, atLeast(0)).execute(captor.capture());
 
         return captor.getAllValues();
     }
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
new file mode 100644
index 0000000..1d9148b
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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
index fc7fe48..fd62d36 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -1,7 +1,6 @@
 package com.android.launcher3.model;
 
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.Point;
@@ -10,9 +9,9 @@
 
 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.config.ProviderConfig;
 import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
 import com.android.launcher3.util.TestLauncherProvider;
 
@@ -40,7 +39,7 @@
     private InvariantDeviceProfile mIdp;
 
     public GridSizeMigrationTaskTest() {
-        super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+        super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
     }
 
     @Override
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index d655562..ed893c4 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -21,9 +21,8 @@
     }
 
     private PackageInstallStateChangedTask newTask(String pkg, int progress) {
-        PackageInstallInfo installInfo = new PackageInstallInfo(pkg);
-        installInfo.progress = progress;
-        installInfo.state = PackageInstallerCompat.STATUS_INSTALLING;
+        int state = PackageInstallerCompat.STATUS_INSTALLING;
+        PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
         return new PackageInstallStateChangedTask(installInfo);
     }
 
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 2ad9b35..9a89b1b 100644
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -28,7 +28,7 @@
 import java.util.Collections;
 import java.util.List;
 
-import static com.android.launcher3.popup.PopupPopulator.MAX_ITEMS;
+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;
@@ -44,18 +44,18 @@
     public void testSortAndFilterShortcuts() {
         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_ITEMS, 0);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_ITEMS);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
-                MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
-                MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_ITEMS - 1, 1);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_ITEMS - 1);
+                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_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
-                MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 29f738b..5858e13 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -47,11 +47,8 @@
     }
 
     private int getCount(SQLiteDatabase db, String sql) {
-        Cursor c = db.rawQuery(sql, null);
-        try {
+        try (Cursor c = db.rawQuery(sql, null)) {
             return c.getCount();
-        } finally {
-            c.getCount();
         }
     }
 
@@ -59,7 +56,7 @@
 
         private final long mProfileId;
 
-        public MyDatabaseHelper(long profileId) {
+        MyDatabaseHelper(long profileId) {
             super(getContext(), null, null);
             mProfileId = profileId;
         }
diff --git a/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java b/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java
new file mode 100644
index 0000000..80d6341
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TouchEventGenerator.java
@@ -0,0 +1,275 @@
+/*
+ * 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.testcomponent;
+
+import android.graphics.Point;
+import android.util.Pair;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class to generate MotionEvent event sequences for testing touch gesture detectors.
+ */
+public class TouchEventGenerator {
+
+    /**
+     * Amount of time between two generated events.
+     */
+    private static final long TIME_INCREMENT_MS = 20L;
+
+    /**
+     * Id of the fake device generating the events.
+     */
+    private static final int DEVICE_ID = 2104;
+
+    /**
+     * The fingers currently present on the emulated touch screen.
+     */
+    private Map<Integer, Point> mFingers;
+
+    /**
+     * Initial event time for the current sequence.
+     */
+    private long mInitialTime;
+
+    /**
+     * Time of the last generated event.
+     */
+    private long mLastEventTime;
+
+    /**
+     * Time of the next event.
+     */
+    private long mTime;
+
+    /**
+     * Receives the generated events.
+     */
+    public interface Listener {
+
+        /**
+         * Called when an event was generated.
+         */
+        void onTouchEvent(MotionEvent event);
+    }
+    private final Listener mListener;
+
+    public TouchEventGenerator(Listener listener) {
+        mListener = listener;
+        mFingers = new HashMap<Integer, Point>();
+    }
+
+    /**
+     * Adds a finger on the touchscreen.
+     */
+    public TouchEventGenerator put(int id, int x, int y, long ms) {
+        checkFingerExistence(id, false);
+        boolean isInitialDown = mFingers.isEmpty();
+        mFingers.put(id, new Point(x, y));
+        int action;
+        if (isInitialDown) {
+            action = MotionEvent.ACTION_DOWN;
+        } else {
+            action = MotionEvent.ACTION_POINTER_DOWN;
+            // Set the id of the changed pointer.
+            action |= id << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        }
+        generateEvent(action, ms);
+        return this;
+    }
+
+    /**
+     * Adds a finger on the touchscreen after advancing default time interval.
+     */
+    public TouchEventGenerator put(int id, int x, int y) {
+        return put(id, x, y, TIME_INCREMENT_MS);
+    }
+
+    /**
+     * Adjusts the position of a finger for an upcoming move event.
+     *
+     * @see #move(long ms)
+     */
+    public TouchEventGenerator position(int id, int x, int y) {
+        checkFingerExistence(id, true);
+        mFingers.get(id).set(x, y);
+        return this;
+    }
+
+    /**
+     * Commits the finger position changes of {@link #position(int, int, int)} by generating a move
+     * event.
+     *
+     * @see #position(int, int, int)
+     */
+    public TouchEventGenerator move(long ms) {
+        generateEvent(MotionEvent.ACTION_MOVE, ms);
+        return this;
+    }
+
+    /**
+     * Commits the finger position changes of {@link #position(int, int, int)} by generating a move
+     * event after advancing the default time interval.
+     *
+     * @see #position(int, int, int)
+     */
+    public TouchEventGenerator move() {
+        return move(TIME_INCREMENT_MS);
+    }
+
+    /**
+     * Moves a single finger on the touchscreen.
+     */
+    public TouchEventGenerator move(int id, int x, int y, long ms) {
+        return position(id, x, y).move(ms);
+    }
+
+    /**
+     * Moves a single finger on the touchscreen after advancing default time interval.
+     */
+    public TouchEventGenerator move(int id, int x, int y) {
+        return move(id, x, y, TIME_INCREMENT_MS);
+    }
+
+    /**
+     * Removes an existing finger from the touchscreen.
+     */
+    public TouchEventGenerator lift(int id, long ms) {
+        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 |= id << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        }
+        generateEvent(action, ms);
+        mFingers.remove(id);
+        return this;
+    }
+
+    /**
+     * Removes a finger from the touchscreen.
+     */
+    public TouchEventGenerator lift(int id, int x, int y, long ms) {
+        checkFingerExistence(id, true);
+        mFingers.get(id).set(x, y);
+        return lift(id, ms);
+    }
+
+    /**
+     * Removes an existing finger from the touchscreen after advancing default time interval.
+     */
+    public TouchEventGenerator lift(int id) {
+        return lift(id, TIME_INCREMENT_MS);
+    }
+
+    /**
+     * Cancels an ongoing sequence.
+     */
+    public TouchEventGenerator cancel(long ms) {
+        generateEvent(MotionEvent.ACTION_CANCEL, ms);
+        mFingers.clear();
+        return this;
+    }
+
+    /**
+     * Cancels an ongoing sequence.
+     */
+    public TouchEventGenerator cancel() {
+        return cancel(TIME_INCREMENT_MS);
+    }
+
+    private void checkFingerExistence(int id, boolean shouldExist) {
+        if (shouldExist != mFingers.containsKey(id)) {
+            throw new IllegalArgumentException(
+                    shouldExist ? "Finger does not exist" : "Finger already exists");
+        }
+    }
+
+    private void generateEvent(int action, long ms) {
+        mTime = mLastEventTime + ms;
+        Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
+        MotionEvent event = MotionEvent.obtain(
+                mInitialTime,
+                mTime,
+                action,
+                state.first.length,
+                state.first,
+                state.second,
+                0 /* metaState */,
+                0 /* buttonState */,
+                1.0f /* xPrecision */,
+                1.0f /* yPrecision */,
+                DEVICE_ID,
+                0 /* edgeFlags */,
+                InputDevice.SOURCE_TOUCHSCREEN,
+                0 /* flags */);
+        mListener.onTouchEvent(event);
+        if (action == MotionEvent.ACTION_UP) {
+            resetTime();
+        }
+        event.recycle();
+        mLastEventTime = mTime;
+    }
+
+    /**
+     * Returns the description of the fingers' state expected by MotionEvent.
+     */
+    private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
+        int nFingers = mFingers.size();
+        PointerProperties[] properties = new PointerProperties[nFingers];
+        PointerCoords[] coordinates = new PointerCoords[nFingers];
+
+        int index = 0;
+        for (Map.Entry<Integer, Point> entry : mFingers.entrySet()) {
+            int id = entry.getKey();
+            Point location = entry.getValue();
+
+            PointerProperties property = new PointerProperties();
+            property.id = id;
+            property.toolType = MotionEvent.TOOL_TYPE_FINGER;
+            properties[index] = property;
+
+            PointerCoords coordinate = new PointerCoords();
+            coordinate.x = location.x;
+            coordinate.y = location.y;
+            coordinate.pressure = 1.0f;
+            coordinates[index] = coordinate;
+
+            index++;
+        }
+
+        return new Pair<MotionEvent.PointerProperties[], MotionEvent.PointerCoords[]>(
+                properties, coordinates);
+    }
+
+    /**
+     * Resets the time references for a new sequence.
+     */
+    private void resetTime() {
+        mInitialTime = 0L;
+        mLastEventTime = -1L;
+        mTime = 0L;
+    }
+}
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
new file mode 100644
index 0000000..ff83131
--- /dev/null
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.touch;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.testcomponent.TouchEventGenerator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SwipeDetectorTest {
+
+    private static final String TAG = SwipeDetectorTest.class.getSimpleName();
+    public static void L(String s, Object... parts) {
+        Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
+    }
+
+    private TouchEventGenerator mGenerator;
+    private SwipeDetector mDetector;
+    private int mTouchSlop;
+
+    @Mock
+    private SwipeDetector.Listener mMockListener;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mGenerator = new TouchEventGenerator(new TouchEventGenerator.Listener() {
+            @Override
+            public void onTouchEvent(MotionEvent event) {
+                mDetector.onTouchEvent(event);
+            }
+        });
+
+        mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.VERTICAL);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+        mTouchSlop = ViewConfiguration.get(InstrumentationRegistry.getTargetContext())
+                .getScaledTouchSlop();
+        L("mTouchSlop=", mTouchSlop);
+    }
+
+    @Test
+    public void testDragStart_vertical() throws Exception {
+        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_failed() throws Exception {
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 + mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener, never()).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_horizontal() throws Exception {
+        mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.HORIZONTAL);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, 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() throws Exception {
+        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(), anyFloat());
+    }
+
+    @Test
+    public void testDragEnd() throws Exception {
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100, 100 + mTouchSlop);
+        mGenerator.move(0, 100, 100 + mTouchSlop * 2);
+        mGenerator.lift(0);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragEnd(anyFloat(), anyBoolean());
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
similarity index 80%
rename from tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
rename to tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 47b43f5..1be33d2 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -15,29 +15,26 @@
  */
 package com.android.launcher3.ui;
 
+import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.pm.LauncherActivityInfo;
 import android.graphics.Point;
-import android.os.ParcelFileDescriptor;
 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.test.InstrumentationTestCase;
 import android.view.MotionEvent;
 
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
@@ -45,25 +42,26 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
+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.util.ManagedProfileHeuristic;
 
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
+import org.junit.Before;
+
 import java.util.Locale;
 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;
+
 /**
  * Base class for all instrumentation tests providing various utility methods.
  */
-public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
+public abstract class AbstractLauncherUiTest {
 
     public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
@@ -71,16 +69,15 @@
     public static final long DEFAULT_UI_TIMEOUT = 3000;
     public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
 
+    protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
     protected UiDevice mDevice;
     protected Context mTargetContext;
     protected String mTargetPackage;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         mDevice = UiDevice.getInstance(getInstrumentation());
-        mTargetContext = getInstrumentation().getTargetContext();
+        mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
     }
 
@@ -97,57 +94,16 @@
         }
     }
 
-    /**
-     * Starts the launcher activity in the target package and returns the Launcher instance.
-     */
-    protected Launcher startLauncher() {
-        return (Launcher) getInstrumentation().startActivitySync(getHomeIntent());
-    }
-
-    protected Intent getHomeIntent() {
-        return new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(mTargetPackage)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    }
-
-    /**
-     * Grants the launcher permission to bind widgets.
-     */
-    protected void grantWidgetPermission() throws IOException {
-        // Check bind widget permission
-        if (mTargetContext.getPackageManager().checkPermission(
-                mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
-                != PackageManager.PERMISSION_GRANTED) {
-            runShellCommand("appwidget grantbind --package " + mTargetPackage);
-        }
-    }
-
-    /**
-     * Sets the target launcher as default launcher.
-     */
-    protected void setDefaultLauncher() throws IOException {
-        ActivityInfo launcher = mTargetContext.getPackageManager()
-                .queryIntentActivities(getHomeIntent(), 0).get(0).activityInfo;
-        runShellCommand("cmd package set-home-activity " +
-                new ComponentName(launcher.packageName, launcher.name).flattenToString());
-    }
-
-    protected void runShellCommand(String command) throws IOException {
-        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
-                .executeShellCommand(command);
-
-        // Read the input stream fully.
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        while (fis.read() != -1);
-        fis.close();
+    protected Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
     }
 
     /**
      * Opens all apps and returns the recycler view
      */
     protected UiObject2 openAllApps() {
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+        mDevice.waitForIdle();
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
             // clicking on the page indicator brings up all apps tray on non tablets.
             findViewById(R.id.page_indicator).click();
         } else {
@@ -262,7 +218,7 @@
 
     protected void resetLoaderState() {
         try {
-            runTestOnUiThread(new Runnable() {
+            mMainThreadExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
@@ -279,7 +235,7 @@
      */
     protected <T> T getOnUiThread(final Callable<T> callback) {
         try {
-            return new MainThreadExecutor().submit(callback).get();
+            return mMainThreadExecutor.submit(callback).get();
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -315,6 +271,10 @@
         return By.res(mTargetPackage, name);
     }
 
+    protected LauncherActivityInfo getSettingsApp() {
+        return LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
+    }
 
     /**
      * Broadcast receiver which blocks until the result is received.
diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
index 0ced7cf..46343a3 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
@@ -1,52 +1,55 @@
 package com.android.launcher3.ui;
 
 import android.content.pm.LauncherActivityInfo;
-import android.os.Process;
+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.test.suitebuilder.annotation.LargeTest;
 
-import com.android.launcher3.compat.LauncherAppsCompat;
 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
-public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AllAppsAppLaunchTest extends AbstractLauncherUiTest {
 
-    private LauncherActivityInfo mSettingsApp;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
-    }
-
+    @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 {
-        startLauncher();
+        mActivityMonitor.startLauncher();
+
+        LauncherActivityInfo settingsApp = getSettingsApp();
 
         // Open all apps and wait for load complete
         final UiObject2 appsContainer = openAllApps();
         assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
 
         // Open settings app and verify app launched
-        scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())).click();
+        scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString())).click();
         assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+                settingsApp.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
index 9361750..00f30ad 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -1,58 +1,62 @@
 package com.android.launcher3.ui;
 
 import android.content.pm.LauncherActivityInfo;
-import android.os.Process;
+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.test.suitebuilder.annotation.LargeTest;
 
-import com.android.launcher3.compat.LauncherAppsCompat;
 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
-public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AllAppsIconToHomeTest extends AbstractLauncherUiTest {
 
-    private LauncherActivityInfo mSettingsApp;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setDefaultLauncher();
-
-        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
-    }
-
+    @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();
-        startLauncher();
+        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(mSettingsApp.getLabel().toString()));
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
         dragToWorkspace(icon, true);
 
         // Verify that the icon works on homescreen.
-        mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click();
+        mDevice.findObject(By.text(settingsApp.getLabel().toString())).click();
         assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+                settingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
deleted file mode 100644
index e84ad04..0000000
--- a/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.SharedPreferences;
-import android.graphics.Rect;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-/**
- * Test for auto rotate preference.
- */
-@MediumTest
-public class RotationPreferenceTest extends LauncherInstrumentationTestCase {
-
-    private SharedPreferences mPrefs;
-    private boolean mOriginalRotationValue;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mDevice = UiDevice.getInstance(getInstrumentation());
-        mTargetContext = getInstrumentation().getTargetContext();
-        mTargetPackage = mTargetContext.getPackageName();
-        mPrefs = Utilities.getPrefs(mTargetContext);
-        mOriginalRotationValue = mPrefs.getBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        setRotationEnabled(mOriginalRotationValue);
-        super.tearDown();
-    }
-
-    public void testRotation_disabled() throws Exception {
-        if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) {
-            // This is a tablet. The test is only valid to mobile devices.
-            return;
-        }
-
-        setRotationEnabled(false);
-        mDevice.setOrientationRight();
-        startLauncher();
-
-        Rect hotseat = getHotseatBounds();
-        assertTrue(hotseat.width() > hotseat.height());
-    }
-
-    public void testRotation_enabled() throws Exception {
-        if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) {
-            // This is a tablet. The test is only valid to mobile devices.
-            return;
-        }
-
-        setRotationEnabled(true);
-        mDevice.setOrientationRight();
-        startLauncher();
-
-        Rect hotseat = getHotseatBounds();
-        assertTrue(hotseat.width() < hotseat.height());
-    }
-
-    private void setRotationEnabled(boolean enabled) {
-        mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit();
-    }
-
-    private Rect getHotseatBounds() throws Exception {
-        UiObject hotseat = mDevice.findObject(
-                new UiSelector().resourceId(mTargetPackage + ":id/hotseat"));
-        hotseat.waitForExists(6000);
-        return hotseat.getVisibleBounds();
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
index 3a0b613..a40ad7f 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -2,54 +2,58 @@
 
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.Point;
-import android.os.Process;
+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.test.suitebuilder.annotation.LargeTest;
 import android.view.MotionEvent;
 
 import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherAppsCompat;
 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
-public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ShortcutsLaunchTest extends AbstractLauncherUiTest {
 
-    private LauncherActivityInfo mSettingsApp;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setDefaultLauncher();
-
-        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
-    }
-
+    @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 {
-        startLauncher();
+        mActivityMonitor.startLauncher();
+        LauncherActivityInfo settingsApp = 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(mSettingsApp.getLabel().toString()));
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
         // Press icon center until shortcuts appear
         Point iconCenter = icon.getVisibleCenter();
         sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
@@ -63,7 +67,7 @@
                 .findObject(getSelectorForId(R.id.bubble_text));
         shortcut.click();
         assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                mSettingsApp.getComponentName().getPackageName())
+                settingsApp.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
index 5d86d1e..434311d 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -2,40 +2,43 @@
 
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.Point;
-import android.os.Process;
+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.test.suitebuilder.annotation.LargeTest;
 import android.view.MotionEvent;
 
 import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherAppsCompat;
 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
-public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ShortcutsToHomeTest extends AbstractLauncherUiTest {
 
-    private LauncherActivityInfo mSettingsApp;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        setDefaultLauncher();
-
-        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
-    }
-
+    @Test
     public void testDragIcon_portrait() throws Throwable {
         lockRotation(true);
         performTest();
     }
 
+    @Test
     public void testDragIcon_landscape() throws Throwable {
         lockRotation(false);
         performTest();
@@ -43,14 +46,16 @@
 
     private void performTest() throws Throwable {
         clearHomescreen();
-        startLauncher();
+        mActivityMonitor.startLauncher();
+
+        LauncherActivityInfo settingsApp  = 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(mSettingsApp.getLabel().toString()));
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
         // Press icon center until shortcuts appear
         Point iconCenter = icon.getVisibleCenter();
         sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
@@ -69,7 +74,7 @@
         // (the app opens and has the same text as the shortcut).
         mDevice.findObject(By.text(shortcutName)).click();
         assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                mSettingsApp.getComponentName().getPackageName())
+                settingsApp.getComponentName().getPackageName())
                 .text(shortcutName)), DEFAULT_UI_TIMEOUT));
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 0b4e34f..a5c2e69 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -15,75 +15,75 @@
  */
 package com.android.launcher3.ui.widget;
 
-import android.app.Activity;
-import android.app.Application;
 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.test.suitebuilder.annotation.LargeTest;
 import android.view.View;
 
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
-import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.SimpleActivityMonitor;
 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 java.util.concurrent.Callable;
+import org.junit.Before;
+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.
  */
 @LargeTest
-public class AddConfigWidgetTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AddConfigWidgetTest extends AbstractLauncherUiTest {
+
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
 
     private LauncherAppWidgetProviderInfo mWidgetInfo;
-    private SimpleActivityMonitor mActivityMonitor;
-    private MainThreadExecutor mMainThreadExecutor;
     private AppWidgetManager mAppWidgetManager;
 
     private int mWidgetId;
 
     @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         super.setUp();
         mWidgetInfo = findWidgetProvider(true /* hasConfigureScreen */);
-        mActivityMonitor = new SimpleActivityMonitor();
-        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
-                .registerActivityLifecycleCallbacks(mActivityMonitor);
-        mMainThreadExecutor = new MainThreadExecutor();
         mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
-
-        grantWidgetPermission();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
-                .unregisterActivityLifecycleCallbacks(mActivityMonitor);
-        super.tearDown();
-    }
-
+    @Test
     public void testWidgetConfig() throws Throwable {
         runTest(false, true);
     }
 
+    @Test
     public void testWidgetConfig_rotate() throws Throwable {
         runTest(true, true);
     }
 
+    @Test
     public void testConfigCancelled() throws Throwable {
         runTest(false, false);
     }
 
+    @Test
     public void testConfigCancelled_rotate() throws Throwable {
         runTest(true, false);
     }
@@ -96,7 +96,7 @@
         lockRotation(true);
 
         clearHomescreen();
-        startLauncher();
+        mActivityMonitor.startLauncher();
 
         // Open widget tray and wait for load complete.
         final UiObject2 widgetContainer = openWidgetsTray();
@@ -146,11 +146,11 @@
      * Condition for searching widget id
      */
     private class WidgetSearchCondition extends Condition
-            implements Callable<Boolean>, Workspace.ItemOperator {
+            implements Workspace.ItemOperator {
 
         @Override
         public boolean isTrue() throws Throwable {
-            return mMainThreadExecutor.submit(this).get();
+            return mMainThreadExecutor.submit(mActivityMonitor.itemExists(this)).get();
         }
 
         @Override
@@ -159,21 +159,6 @@
                     ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) &&
                     ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
         }
-
-        @Override
-        public Boolean call() throws Exception {
-            // Find the resumed launcher
-            Launcher launcher = null;
-            for (Activity a : mActivityMonitor.resumed) {
-                if (a instanceof Launcher) {
-                    launcher = (Launcher) a;
-                }
-            }
-            if (launcher == null) {
-                return false;
-            }
-            return launcher.getWorkspace().getFirstMatch(this) != null;
-        }
     }
 
     /**
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 3c92c57..19f7db7 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -15,42 +15,46 @@
  */
 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 android.test.suitebuilder.annotation.LargeTest;
 import android.view.View;
 
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+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.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
 /**
  * Test to add widget from widget tray
  */
 @LargeTest
-public class AddWidgetTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AddWidgetTest extends AbstractLauncherUiTest {
 
-    private LauncherAppWidgetProviderInfo widgetInfo;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        grantWidgetPermission();
-
-        widgetInfo = findWidgetProvider(false /* hasConfigureScreen */);
-    }
-
+    @Test
     public void testDragIcon_portrait() throws Throwable {
         lockRotation(true);
         performTest();
     }
 
+    @Test
     public void testDragIcon_landscape() throws Throwable {
         lockRotation(false);
         performTest();
@@ -58,7 +62,10 @@
 
     private void performTest() throws Throwable {
         clearHomescreen();
-        Launcher launcher = startLauncher();
+        mActivityMonitor.startLauncher();
+
+        final LauncherAppWidgetProviderInfo widgetInfo =
+                findWidgetProvider(false /* hasConfigureScreen */);
 
         // Open widget tray and wait for load complete.
         final UiObject2 widgetContainer = openWidgetsTray();
@@ -69,12 +76,12 @@
                 .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
         dragToWorkspace(widget, false);
 
-        assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() {
+        assertTrue(mActivityMonitor.itemExists(new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View view) {
                 return info instanceof LauncherAppWidgetInfo &&
                         ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
             }
-        }));
+        }).call());
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index df2b662..d4d517a 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -24,10 +24,11 @@
 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 android.test.suitebuilder.annotation.LargeTest;
 
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetHostView;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -37,24 +38,40 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.LooperExecuter;
+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.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.Set;
 import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
 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.
  *
  * Note running these tests will clear the workspace on the device.
  */
 @LargeTest
-public class BindWidgetTest extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class BindWidgetTest extends AbstractLauncherUiTest {
+
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
 
     private ContentResolver mResolver;
     private AppWidgetManagerCompat mWidgetManager;
@@ -65,21 +82,20 @@
     private int mSessionId = -1;
 
     @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         super.setUp();
 
         mResolver = mTargetContext.getContentResolver();
         mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
-        grantWidgetPermission();
 
         // Clear all existing data
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         if (mCursor != null) {
             mCursor.close();
         }
@@ -89,6 +105,7 @@
         }
     }
 
+    @Test
     public void testBindNormalWidget_withConfig() {
         LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
@@ -96,6 +113,7 @@
         setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
     }
 
+    @Test
     public void testBindNormalWidget_withoutConfig() {
         LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
@@ -103,6 +121,7 @@
         setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
     }
 
+    @Test
     public void testUnboundWidget_removed() throws Exception {
         LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
@@ -121,6 +140,7 @@
         assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
     }
 
+    @Test
     public void testPendingWidget_autoRestored() {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
@@ -132,6 +152,7 @@
         setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
     }
 
+    @Test
     public void testPendingWidget_withConfigScreen() throws Exception {
         // A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
         LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
@@ -154,6 +175,7 @@
                 LauncherSettings.Favorites.APPWIDGET_ID))));
     }
 
+    @Test
     public void testPendingWidget_notRestored_removed() throws Exception {
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
@@ -170,6 +192,7 @@
         assertEquals(0, mCursor.getCount());
     }
 
+    @Test
     public void testPendingWidget_notRestored_brokenInstall() throws Exception {
         // A widget which is was being installed once, even if its not being
         // installed at the moment is not removed.
@@ -192,6 +215,7 @@
                         & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
     }
 
+    @Test
     public void testPendingWidget_notRestored_activeInstall() throws Exception {
         // A widget which is being installed is not removed
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
@@ -250,7 +274,7 @@
         resetLoaderState();
 
         // Launch the home activity
-        startLauncher();
+        mActivityMonitor.startLauncher();
         // Verify UI
         UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
                 .className(widgetClass);
@@ -286,7 +310,7 @@
             pendingInfo.minSpanY = item.minSpanY;
             Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
 
-            AppWidgetHost host = new AppWidgetHost(mTargetContext, Launcher.APPWIDGET_HOST_ID);
+            AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext);
             int widgetId = host.allocateAppWidgetId();
             if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
                 host.deleteAppWidgetId(widgetId);
@@ -340,7 +364,7 @@
      * Blocks the current thread until all the jobs in the main worker thread are complete.
      */
     private void waitUntilLoaderIdle() throws Exception {
-        new LooperExecuter(LauncherModel.getWorkerLooper())
+        new LooperExecutor(LauncherModel.getWorkerLooper())
                 .submit(new Runnable() {
                     @Override
                     public void run() { }
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index b798dfa..bd21315 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -15,23 +15,20 @@
  */
 package com.android.launcher3.ui.widget;
 
-import android.app.Activity;
-import android.app.Application;
 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.test.suitebuilder.annotation.LargeTest;
 import android.view.View;
 
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
@@ -40,50 +37,48 @@
 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
 import com.android.launcher3.testcomponent.RequestPinItemActivity;
-import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.SimpleActivityMonitor;
 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.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.UUID;
-import java.util.concurrent.Callable;
+
+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
-public class RequestPinItemTest  extends LauncherInstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class RequestPinItemTest  extends AbstractLauncherUiTest {
 
-    private SimpleActivityMonitor mActivityMonitor;
-    private MainThreadExecutor mMainThreadExecutor;
+    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
+    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
 
     private String mCallbackAction;
     private String mShortcutId;
     private int mAppWidgetId;
 
     @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         super.setUp();
-        grantWidgetPermission();
-        setDefaultLauncher();
-
-        mActivityMonitor = new SimpleActivityMonitor();
-        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
-                .registerActivityLifecycleCallbacks(mActivityMonitor);
-        mMainThreadExecutor = new MainThreadExecutor();
-
         mCallbackAction = UUID.randomUUID().toString();
         mShortcutId = UUID.randomUUID().toString();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
-                .unregisterActivityLifecycleCallbacks(mActivityMonitor);
-        super.tearDown();
-    }
-
+    @Test
     public void testPinWidgetNoConfig() throws Throwable {
         runTest("pinWidgetNoConfig", true, new ItemOperator() {
             @Override
@@ -96,6 +91,7 @@
         });
     }
 
+    @Test
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command =  RequestPinItemActivity.getCommandIntent(
@@ -113,6 +109,7 @@
         }, command);
     }
 
+    @Test
     public void testPinWidgetWithConfig() throws Throwable {
         runTest("pinWidgetWithConfig", true, new ItemOperator() {
             @Override
@@ -125,6 +122,7 @@
         });
     }
 
+    @Test
     public void testPinShortcut() throws Throwable {
         // Command to set the shortcut id
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -143,13 +141,13 @@
 
     private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
             Intent... commandIntents) throws Throwable {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         lockRotation(true);
 
         clearHomescreen();
-        startLauncher();
+        mActivityMonitor.startLauncher();
 
         // Open all apps and wait for load complete
         final UiObject2 appsContainer = openAllApps();
@@ -191,14 +189,14 @@
         }
 
         // Go back to home
-        mTargetContext.startActivity(getHomeIntent());
+        mActivityMonitor.returnToHome();
         assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT));
     }
 
     /**
      * Condition for for an item
      */
-    private class ItemSearchCondition extends Condition implements Callable<Boolean> {
+    private class ItemSearchCondition extends Condition {
 
         private final ItemOperator mOp;
 
@@ -208,22 +206,7 @@
 
         @Override
         public boolean isTrue() throws Throwable {
-            return mMainThreadExecutor.submit(this).get();
-        }
-
-        @Override
-        public Boolean call() throws Exception {
-            // Find the resumed launcher
-            Launcher launcher = null;
-            for (Activity a : mActivityMonitor.resumed) {
-                if (a instanceof Launcher) {
-                    launcher = (Launcher) a;
-                }
-            }
-            if (launcher == null) {
-                return false;
-            }
-            return launcher.getWorkspace().getFirstMatch(mOp) != null;
+            return mMainThreadExecutor.submit(mActivityMonitor.itemExists(mOp)).get();
         }
     }
 }
diff --git a/tests/src/com/android/launcher3/util/SimpleActivityMonitor.java b/tests/src/com/android/launcher3/util/SimpleActivityMonitor.java
deleted file mode 100644
index 6154ab6..0000000
--- a/tests/src/com/android/launcher3/util/SimpleActivityMonitor.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.util;
-
-import android.app.Activity;
-import android.app.Application.*;
-import android.os.Bundle;
-
-import java.util.ArrayList;
-
-/**
- * Simple monitor to keep a list of active activities.
- */
-public class SimpleActivityMonitor implements ActivityLifecycleCallbacks {
-
-    public final ArrayList<Activity> created = new ArrayList<>();
-    public final ArrayList<Activity> started = new ArrayList<>();
-    public final ArrayList<Activity> resumed = new ArrayList<>();
-
-    @Override
-    public void onActivityCreated(Activity activity, Bundle bundle) {
-        created.add(activity);
-    }
-
-    @Override
-    public void onActivityStarted(Activity activity) {
-        started.add(activity);
-    }
-
-    @Override
-    public void onActivityResumed(Activity activity) {
-        resumed.add(activity);
-    }
-
-    @Override
-    public void onActivityPaused(Activity activity) {
-        resumed.remove(activity);
-    }
-
-    @Override
-    public void onActivityStopped(Activity activity) {
-        started.remove(activity);
-    }
-
-    @Override
-    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
-
-    @Override
-    public void onActivityDestroyed(Activity activity) {
-        created.remove(activity);
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 6ca2121..1d6c18b 100644
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.util;
 
 import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 
 import com.android.launcher3.LauncherProvider;
@@ -43,5 +44,8 @@
 
         @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/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
new file mode 100644
index 0000000..edd152a
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -0,0 +1,132 @@
+/*
+ * 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.rule;
+
+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 com.android.launcher3.Launcher;
+import com.android.launcher3.Workspace.ItemOperator;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Test rule to get the current Launcher activity.
+ */
+public class LauncherActivityRule implements TestRule {
+
+    private Launcher mActivity;
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new MyStatement(base);
+    }
+
+    public Launcher getActivity() {
+        return mActivity;
+    }
+
+    public Callable<Boolean> itemExists(final ItemOperator op) {
+        return new Callable<Boolean>() {
+
+            @Override
+            public Boolean call() throws Exception {
+                Launcher launcher = getActivity();
+                if (launcher == null) {
+                    return false;
+                }
+                return launcher.getWorkspace().getFirstMatch(op) != null;
+            }
+        };
+    }
+
+    /**
+     * 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);
+    }
+
+    private class MyStatement extends Statement implements ActivityLifecycleCallbacks {
+
+        private final Statement mBase;
+
+        public MyStatement(Statement base) {
+            mBase = base;
+        }
+
+        @Override
+        public void evaluate() throws Throwable {
+            Application app = (Application)
+                    InstrumentationRegistry.getTargetContext().getApplicationContext();
+            app.registerActivityLifecycleCallbacks(this);
+            try {
+                mBase.evaluate();
+            } finally {
+                app.unregisterActivityLifecycleCallbacks(this);
+            }
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle bundle) {
+            if (activity instanceof Launcher) {
+                mActivity = (Launcher) activity;
+            }
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) { }
+
+        @Override
+        public void onActivityResumed(Activity activity) { }
+
+        @Override
+        public void onActivityPaused(Activity activity) { }
+
+        @Override
+        public void onActivityStopped(Activity activity) { }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            if (activity == mActivity) {
+                mActivity = null;
+            }
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
new file mode 100644
index 0000000..dba2d71
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -0,0 +1,90 @@
+/*
+ * 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.rule;
+
+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;
+
+/**
+ * Test rule which executes a shell command at the start of the test.
+ */
+public class ShellCommandRule implements TestRule {
+
+    private final String mCmd;
+
+    public ShellCommandRule(String cmd) {
+        mCmd = cmd;
+    }
+
+    @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();
+        }
+    }
+
+    /**
+     * Grants the launcher permission to bind widgets.
+     */
+    public static ShellCommandRule grandWidgetBind() {
+        return new ShellCommandRule("appwidget grantbind --package "
+                + InstrumentationRegistry.getTargetContext().getPackageName());
+    }
+
+    /**
+     * 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());
+    }
+}
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
new file mode 100644
index 0000000..40b65e4
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget;
+
+import android.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.view.LayoutInflater;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.util.MultiHashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WidgetsListAdapterTest {
+
+    private final String TAG = "WidgetsListAdapterTest";
+
+    @Mock private LayoutInflater mMockLayoutInflater;
+    @Mock private WidgetPreviewLoader mMockWidgetCache;
+    @Mock private WidgetsDiffReporter.NotifyListener mListener;
+    @Mock private IconCache mIconCache;
+
+    private WidgetsListAdapter mAdapter;
+    private AlphabeticIndexCompat mIndexCompat;
+    private InvariantDeviceProfile mTestProfile;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+        mIndexCompat = new AlphabeticIndexCompat(mContext);
+        WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache);
+        reporter.setListener(mListener);
+        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
+                mIndexCompat, null, null, reporter);
+    }
+
+    @Test
+    public void test_notifyDataSetChanged() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+    }
+
+    @Test
+    public void test_notifyItemInserted() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(2));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemInserted(1);
+    }
+
+    @Test
+    public void test_notifyItemRemoved() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(2));
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemRemoved(1);
+    }
+
+    @Test
+    public void testNotifyItemChanged_PackageIconDiff() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemChanged(0);
+    }
+
+    @Test
+    public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
+        // TODO: same package name but item number changed
+    }
+
+    @Test
+    public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
+        // TODO: insert and remove combined.          curMap
+        // newMap [A, C, D]                           [A, B, E]
+        // B - C < 0, removed B from index 1          [A, E]
+        // E - C > 0, C inserted to index 1           [A, C, E]
+        // E - D > 0, D inserted to index 2           [A, C, D, E]
+        // E - null = -1, E deleted from index 3      [A, C, D]
+    }
+
+    /**
+     * Helper method to generate the sample widget model map that can be used for the tests
+     * @param num the number of WidgetItem the map should contain
+     * @return
+     */
+    private MultiHashMap<PackageItemInfo, WidgetItem> generateSampleMap(int num) {
+        MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
+        if (num <= 0) return newMap;
+
+        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);
+
+            PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
+            pInfo.title = pInfo.packageName;
+            pInfo.user = wi.user;
+            pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
+            newMap.addToList(pInfo, wi);
+            if (newMap.size() == num) {
+                break;
+            }
+        }
+        return newMap;
+    }
+}