Merge "Launcher3: Can't search out local app by Chinese" am: 7c35c64a9d am: fa5511133a am: 788000b0ec
am: 29e4a28f8c

Change-Id: I2ffeab12ebdd4422fb92c263fa7de927bf8bd506
diff --git a/Android.mk b/Android.mk
index 713d082..c8a53d2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,11 +26,13 @@
 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-java-files-under, src_flags) \
     $(call all-proto-files-under, protos)
 
 LOCAL_RESOURCE_DIR := \
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/build.gradle b/build.gradle
index 9c71693..c23a299 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,7 +39,7 @@
     sourceSets {
         main {
             res.srcDirs = ['res']
-            java.srcDirs = ['src', 'src_config']
+            java.srcDirs = ['src', 'src_flags']
             manifest.srcFile 'AndroidManifest-common.xml'
             proto.srcDirs 'protos/'
         }
@@ -68,6 +68,7 @@
 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'
@@ -92,6 +93,7 @@
                     remove java
                     javanano {
                         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/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/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%
rename from res/drawable/bg_white_round_rect.xml
rename 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%
rename from res/layout/qsb_blocker_view.xml
rename 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/all_apps_alpha_mask.png b/res/drawable/all_apps_alpha_mask.png
new file mode 100644
index 0000000..ed53ff9
--- /dev/null
+++ b/res/drawable/all_apps_alpha_mask.png
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/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_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_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/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_interpolator.xml b/res/interpolator/large_folder_preview_item_interpolator.xml
new file mode 100644
index 0000000..dcf0157
--- /dev/null
+++ b/res/interpolator/large_folder_preview_item_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"
+    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..957c331
--- /dev/null
+++ b/res/layout-land/all_apps_fast_scroller.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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="-5dp" />
+
+    <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_alignTop="@+id/apps_list_view"
+        android:layout_marginEnd="-48dp"
+        android:layout_marginTop="-8dp"
+        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..2bbd76d 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_scrim" />
+
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
@@ -59,16 +62,11 @@
 
         <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:theme="@style/HomeScreenElementTheme"
+            android:layout_width="@dimen/dynamic_grid_page_indicator_size"
+            android:layout_height="@dimen/dynamic_grid_page_indicator_size"
             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"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index dd981dd..93c9078 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_scrim" />
+
         <!-- 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..ca4d846 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_scrim" />
+
         <!-- 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..93662fc 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">
@@ -51,46 +54,17 @@
             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" />
 
-        <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
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_container.xml b/res/layout/all_apps_search_container.xml
new file mode 100644
index 0000000..2528034
--- /dev/null
+++ b/res/layout/all_apps_search_container.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/all_apps_search_bar_height"
+    android:layout_gravity="center|top"
+    android:gravity="center|bottom"
+    android:saveEnabled="false"
+    android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+    android:paddingRight="@dimen/dynamic_grid_edge_margin"
+    android:layout_marginBottom="-8dp" >
+
+    <!--
+      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: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/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/gradient_scrim.xml b/res/layout/gradient_scrim.xml
new file mode 100644
index 0000000..c40c5fc
--- /dev/null
+++ b/res/layout/gradient_scrim.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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:app="http://schemas.android.com/apk/res-auto">
+    <com.android.launcher3.graphics.GradientView
+        android:id="@+id/gradient_bg"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:layout_ignoreInsets="true"/>
+
+    <com.android.launcher3.graphics.ScrimView
+        android:id="@+id/scrim_bg"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:layout_ignoreInsets="true"/>
+</merge>
\ No newline at end of file
diff --git a/res/layout/horizontal_divider.xml b/res/layout/horizontal_divider.xml
new file mode 100644
index 0000000..167f8f5
--- /dev/null
+++ b/res/layout/horizontal_divider.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/popup_item_divider_height"
+    android:background="?attr/popupColorTertiary" />
\ 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..085dfa9 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"
@@ -35,7 +34,7 @@
             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:background="?attr/popupColorPrimary"
             android:elevation="@dimen/notification_elevation">
             <TextView
                 android:id="@+id/notification_text"
@@ -67,8 +66,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"
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..7a8cf6d 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -28,9 +28,10 @@
         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="16dp">
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
@@ -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="8dp"
         android:layout_gravity="center_vertical|end" />
 
 </com.android.launcher3.notification.NotificationMainView>
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..14ff2bd 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_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_gravity="top|center"
             android:scaleType="centerInside"/>
 </com.android.launcher3.pageindicators.PageIndicatorLineCaret>
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/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..4d80aac 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -24,6 +24,7 @@
 
     <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"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2063f32..e91f966 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -24,6 +24,7 @@
 
     <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"
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..fe00d7a 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Gebruik stelselverstek"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Sirkelvierkant"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traandruppel"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 59c44e2..aebb428 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"የሥርዓቱን ነባሪ ተጠቀም"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"ካሬ"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"ክብ"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"የእንባ ጠብታ"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index d8b1cc9..a45e40e 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index e5d0a6c..a8791b7 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kənarları dairəvi kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Çevrə"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 401fef3..cb296f4 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Koristi podrazumevano sistemsko podešavanje"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index c19a8b4..d5df3f2 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 70e2c9b..d4c5210 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -70,19 +70,23 @@
     <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="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..d91106b 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 9913d6d..88b02bb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -70,20 +70,24 @@
     <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">"Activada"</string>
+    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</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_system_default" msgid="1709762974822753030">"Utilitza l\'opció predeterminada del sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrat arrodonit"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Llàgrima"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 03c109d..1a80e39 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -70,20 +70,24 @@
     <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ími"</string>
+    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuto"</string>
+    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Vypnuto"</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_system_default" msgid="1709762974822753030">"Použít výchozí nastavení systému"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Čtverec"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kruh/čtverec"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c16b8c1..17316b3 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Brug systemstandarden"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrat med runde hjørner"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråbeform"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c627b74..72f7399 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Systemstandardeinstellung verwenden"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Superkreis"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Kreis"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Träne"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 9f5c01f..406101c 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 1a1c1c9..4e0ba03 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Use system default"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 1a1c1c9..4e0ba03 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Use system default"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 1a1c1c9..4e0ba03 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Use system default"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 28d8be3..bef4757 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Usar el sistema predeterminado"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gota"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index badb33e..e49ac3f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Usar opción predeterminada del sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index aa9c796..59fc221 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Karratua"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ertz biribilduko karratua"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Zirkulua"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Malkoa"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index e66ea80..4edc4e7 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index a7db232..f24cbae 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Käytä järjestelmän oletusarvoa"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Neliö"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ympyräneliö"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Ympyrä"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pisara"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 03757e0..5a3bd54 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Utiliser les valeurs système par défaut"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Carré aux coins ronds"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 75a693c..2897af0 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -70,20 +70,24 @@
     <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 du domicile"</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="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_system_default" msgid="1709762974822753030">"Utiliser la valeur système par défaut"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 4bff56f..9211ba1 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Cadrado"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cadrado de bordos redondeados"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Bágoa"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index b243da7..b491e42 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -70,19 +70,23 @@
     <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="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..371f6b7 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 4b6a0bd..0912677 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Upotrijebi zadane postavke sustava"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index c5ac907..3ffd508 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Alapértelmezett érték használata"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Négyzet"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Kör"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Könnycsepp"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 46e5682..1646f9e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index fa7b878..5634963 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Gunakan default sistem"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Persegi"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Persegi bundar"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Lingkaran"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Butiran Air"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 1d7a511..853cd3b 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Ferningur"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ferhringur"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Hringur"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dropi"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a63d495..8af098d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Usa impostazione predefinita di sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Quadrato"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Supercerchio"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cerchio"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goccia"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7f960f2..2425254 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 6182eb5..cb5fa15 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7ab510e..2634cdf 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index c49ef3b..66766b6 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 82bf4bb..ee44302 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 6e61af5..6c89b7b 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -70,19 +70,23 @@
     <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="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..26535f0 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index bdfd15c..cc15f04 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 3915759..08073ce 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,24 @@
 -->
 
 <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_page_indicator_size">24dp</dimen>
+    <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
+    <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
+
+    <!-- Hotseat -->
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">24dp</dimen>
+    <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">32dp</dimen>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 799db3c..0b29434 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index bbf7f1a..945cbf6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Naudoti numatytuosius sistemos nustatymus"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadratas"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadratais suapvalintais kampais"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Apskritimas"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Ašara"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 75e1bef..e3c3482 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Izmantot sistēmas noklusējumu"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrāts"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrāts ar noapaļotiem stūriem"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Aplis"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lāse"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index f0c1b64..abd1d35 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 9dc4bb1..bad7e48 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -70,19 +70,23 @@
     <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="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..264e55a 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a7a8c80..b23ea96 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -70,19 +70,23 @@
     <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="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-my/strings.xml b/res/values-my/strings.xml
index c8bad18..a4648b1 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e75e831..2b776d6 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Bruk systemstandard"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Superellipse"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråpe"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index dee84e6..558b6cf 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -70,19 +70,23 @@
     <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="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..0180bb4 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Systeemstandaard gebruiken"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traan"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 8e51bd3..05fe509 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -70,19 +70,23 @@
     <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="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-pl/strings.xml b/res/values-pl/strings.xml
index 9192732..f8a7f66 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Użyj ustawienia domyślnego"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kwadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaokrąglony kwadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Okrąg"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Łza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 605dff2..5c37eb8 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Utilizar a predefinição do sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado e círculo"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 7945b07..fd24066 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Usar padrão do sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado arredondado"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b433029..e7b8144 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Folosiți setarea prestabilită a sistemului"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Pătrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Pătrat cu colțuri rotunjite"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cerc"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lacrimă"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6c87042..07ff761 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index f8f8e85..3b0425e 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 3d754b2..b7999f4 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Použiť predvolené nastavenie systému"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Štvorec"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Okrúhly štvorec"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index fddb445..927b389 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Uporabi privzeto nastavitev sistema"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljen kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Krog"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Solza"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index f300d03..1400557 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sisteit"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Katror"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Katror me kënde të rrumbullakëta"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Rreth"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pikë loti"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 68cb442..2cfaee6 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 4dd6f48..aa6e99a 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Använd systemstandard"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvirkel"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Droppe"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index ce06bdb..3b6d373 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -72,20 +72,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Tumia umbo chaguo-msingi la mfumo"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Mraba"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Mstatili wenye pembe duara"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Mduara"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Umbo la chozi"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 358d9b6..e836d7d 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -15,10 +15,16 @@
 -->
 
 <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..bb0dbc2 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/all_apps_search_container</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..be8e82d 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 338c158..15f3de7 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -70,19 +70,23 @@
     <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="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-th/strings.xml b/res/values-th/strings.xml
index 65702a0..e03931e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 8270708..12bc013 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Gamitin ang default ng system"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Parisukat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Bilog"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index da0aa28..b245bff 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Sistem varsayılanını kullan"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kare"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kare-daire"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Daire"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a9cefaf..8d33910 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 195a107..84966b2 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -70,19 +70,23 @@
     <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="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..a4725db 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Qirralari aylana kvadrat"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Aylana"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tomchi"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</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..fd6fc4d 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -21,6 +21,10 @@
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
+    <style name="WidgetContainerTheme.Dark" parent="LauncherThemeDark">
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+        <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+    </style>
 
     <!-- From O and above, we show a dark nav bar in all-apps -->
     <style name="AllAppsNavBarProtection">
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 626ea99..f6cb8f2 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Sử dụng mặc định của hệ thống"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Hình vuông"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"Hình vuông cạnh bo tròn"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Hình tròn"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Hình giọt nước"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index fde1986..5dcef74 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index d5cf997..2bbc160 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 276ddec..f1605f6 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -70,20 +70,24 @@
     <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="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_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>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 82fd4cf..12d14bd 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -70,20 +70,24 @@
     <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="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_system_default" msgid="1709762974822753030">"Sebenzisa okuzenzakalelayo kwesistimu"</string>
+    <string name="icon_shape_square" msgid="633575066111622774">"Isikwele"</string>
+    <string name="icon_shape_squircle" msgid="5658049910802669495">"I-Squircle"</string>
+    <string name="icon_shape_circle" msgid="6550072265930144217">"Indingiliza"</string>
+    <string name="icon_shape_teardrop" msgid="4525869388200835463">"I-Teardrop"</string>
     <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
     <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 18f409f..7b52dae 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -16,7 +16,21 @@
 */
 -->
 
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Attributes used for launcher theme -->
+    <attr name="allAppsScrimColor" 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 +44,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,9 +117,9 @@
         <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" />
     </declare-styleable>
@@ -111,4 +132,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..db1a75d 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -13,6 +13,10 @@
      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>
+
     <!-- 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 +30,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 +64,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 +84,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 +108,15 @@
     <!-- 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>
+
     <!-- 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 +124,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">220</integer>
+    <integer name="config_popupArrowOpenDuration">80</integer>
     <integer name="config_removeNotificationViewDuration">300</integer>
 
 <!-- Accessibility actions -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index bd6466b..a4dff71 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -16,24 +16,32 @@
 
 <resources>
 <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_edge_margin">8dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_height">28dp</dimen>
+    <dimen name="dynamic_grid_edge_margin">16dp</dimen>
+    <dimen name="dynamic_grid_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_gutter_width">50dp</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>
 
+    <!-- dynamic_grid_edge_margin / 2 -->
+    <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_height">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>
+
 <!-- Drop target bar -->
     <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
     <dimen name="vert_drop_target_vertical_gap">20dp</dimen>
@@ -44,27 +52,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 +84,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 +121,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 +136,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 +165,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>
@@ -196,7 +209,7 @@
     <!-- 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_main_height">96dp</dimen>
     <dimen name="notification_footer_height">32dp</dimen>
     <dimen name="notification_header_text_size">13sp</dimen>
     <dimen name="notification_header_count_text_size">12sp</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..da6da04 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -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. -->
@@ -188,6 +188,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..d11b002 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -25,9 +25,60 @@
         <item name="android:windowShowWallpaper">true</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:colorEdgeEffect">#FF757575</item>
+        <item name="android:keyboardLayout">@layout/all_apps_search_container</item>
     </style>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+    <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
+        <item name="allAppsScrimColor">#DDFFFFFF</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">#33000000</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 +87,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>
@@ -70,15 +123,22 @@
 
         <!-- 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>
 
     <!-- Drop targets -->
@@ -86,15 +146,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..c76f118 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -16,6 +16,18 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <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_add_icon_to_home"
         android:title="@string/auto_add_shortcuts_label"
@@ -24,6 +36,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 +52,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/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 84a8bce..70be7da 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -12,6 +12,7 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.LoaderTask;
 import com.android.launcher3.util.ContentWriter;
 
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -52,7 +53,7 @@
 
             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 {
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..2b59ede 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -22,11 +22,13 @@
 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 +56,11 @@
         }
         return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
     }
+
+    public SystemUiController getSystemUiController() {
+        if (mSystemUiController == null) {
+            mSystemUiController = new SystemUiController(getWindow());
+        }
+        return mSystemUiController;
+    }
 }
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index ac7cbaf..c55a586 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -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..6f2c897 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;
@@ -43,7 +44,7 @@
 import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
 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 +60,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 +69,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 +106,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 +140,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 +152,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 +168,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) {
@@ -206,6 +199,10 @@
         // Verify high res immediately
         verifyHighRes();
 
+        if (info instanceof PromiseAppInfo) {
+            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+            applyProgressLevel(promiseAppInfo.level);
+        }
         applyBadgeState(info, false /* animate */);
     }
 
@@ -238,19 +235,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 +263,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 +360,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 +375,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 +414,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 +427,6 @@
     }
 
     @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (mBackground != null) mBackground.setCallback(null);
-    }
-
-    @Override
     public void setTextColor(int color) {
         mTextColor = color;
         super.setTextColor(color);
@@ -524,14 +439,29 @@
     }
 
     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) {
+        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, fadeIn ? Color.alpha(mTextColor) : 0);
+    }
+
     @Override
     public void cancelLongPress() {
         super.cancelLongPress();
@@ -547,27 +477,36 @@
                     ((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 +542,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) {
@@ -634,7 +587,9 @@
                 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 +621,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..e4a3226 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -102,8 +102,8 @@
     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) {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8179dad..d99a30a 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.
@@ -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..975675a 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
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e47031a..dcfb268 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;
 
@@ -80,9 +80,8 @@
     public final int workspaceSpringLoadedBottomSpace;
 
     // Page indicator
-    private final int pageIndicatorHeightPx;
-    private final int pageIndicatorLandGutterLeftNavBarPx;
-    private final int pageIndicatorLandGutterRightNavBarPx;
+    private final int pageIndicatorSizePx;
+    private final int pageIndicatorLandGutterPx;
     private final int pageIndicatorLandWorkspaceOffsetPx;
 
     // Workspace icons
@@ -93,6 +92,7 @@
 
     public int cellWidthPx;
     public int cellHeightPx;
+    public int workspaceCellPaddingXPx;
 
     // Folder
     public int folderBackgroundOffset;
@@ -109,15 +109,17 @@
     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;
+    public int hotseatBarTopPaddingPx;
+    public int hotseatBarLeftNavBarRightPaddingPx;
+    public int hotseatBarRightNavBarRightPaddingPx;
+    public int hotseatBarBottomPaddingPx;
+    public int hotseatLandLeftNavBarGutterPx;
+    public int hotseatLandRightNavBarGutterPx;
 
     // All apps
+    public int allAppsCellHeightPx;
     public int allAppsNumCols;
     public int allAppsNumPredictiveCols;
     public int allAppsButtonVisualSize;
@@ -159,19 +161,22 @@
         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);
+        pageIndicatorSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_size);
+        pageIndicatorLandGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_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 +196,26 @@
         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);
+        hotseatBarHeightPx = isVerticalBarLayout()
+                ? res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
+
+        hotseatLandLeftNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_left_nav_bar_gutter_width);
+        hotseatLandRightNavBarGutterPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_hotseat_land_right_nav_bar_gutter_width);
 
         // Determine sizes.
         widthPx = width;
@@ -228,9 +248,6 @@
         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;
-
         // 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.
         float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
@@ -259,45 +276,51 @@
     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) {
+        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);
+        iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
+
+        // All apps
+        allAppsIconTextSizePx = iconTextSizePx;
         allAppsIconSizePx = iconSizePx;
         allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
-        allAppsIconTextSizePx = iconTextSizePx;
+        allAppsCellHeightPx = getCellSize().y;
 
-        cellWidthPx = iconSizePx;
+        if (isVerticalBarLayout()) {
+            // Always hide the Workspace text with vertical bar layout.
+            iconTextSizePx = 0;
+            allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+        }
+
+        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
 
         // Hotseat
-        hotseatCellWidthPx = iconSizePx;
-        hotseatCellHeightPx = iconSizePx;
+        hotseatCellHeightPx = iconSizePx + iconDrawablePaddingPx;
 
         if (!isVerticalBarLayout()) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
-                    - pageIndicatorHeightPx - topWorkspacePadding;
+                    - pageIndicatorSizePx - topWorkspacePadding;
             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
             workspaceSpringLoadShrinkFactor = Math.min(
                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -308,7 +331,7 @@
         }
 
         // Folder icon
-        folderBackgroundOffset = -edgeMarginPx;
+        folderBackgroundOffset = -iconDrawablePaddingPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
         folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
     }
@@ -401,22 +424,25 @@
 
     /**
      * 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 + pageIndicatorLandGutterPx,
+                        0,
+                        hotseatBarHeightPx + hotseatLandLeftNavBarGutterPx
+                                + hotseatBarLeftNavBarRightPaddingPx - mInsets.left,
+                        edgeMarginPx);
             } else {
-                padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
-                        hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
+                padding.set(pageIndicatorLandGutterPx,
+                        0,
+                        hotseatBarHeightPx + hotseatLandRightNavBarGutterPx
+                                + hotseatBarRightNavBarRightPaddingPx,
+                        edgeMarginPx);
             }
         } else {
-            int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
+            int paddingBottom = hotseatBarHeightPx + pageIndicatorSizePx;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
@@ -458,7 +484,7 @@
             return new Rect(mInsets.left,
                     mInsets.top + dropTargetBarSizePx + edgeMarginPx,
                     mInsets.left + availableWidthPx,
-                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
+                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorSizePx -
                             edgeMarginPx);
         }
     }
@@ -522,7 +548,7 @@
         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
         lp.width = searchBarBounds.x;
         lp.height = searchBarBounds.y;
-        lp.topMargin = mInsets.top + edgeMarginPx;
+        lp.topMargin = mInsets.top + edgeMarginPx / 2;
         searchBar.setLayoutParams(lp);
 
         // Layout the workspace
@@ -532,14 +558,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();
@@ -556,7 +574,12 @@
             lp.gravity = Gravity.RIGHT;
             lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
+
+            int paddingRight = mInsets.left > 0
+                    ? hotseatBarLeftNavBarRightPaddingPx
+                    : hotseatBarRightNavBarRightPaddingPx;
+
+            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right + paddingRight,
                     workspacePadding.bottom);
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
@@ -584,17 +607,15 @@
             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.height = pageIndicatorSizePx;
                 lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
             }
             pageIndicator.setLayoutParams(lp);
@@ -613,6 +634,12 @@
             overviewMode.setLayoutParams(lp);
         }
 
+        // Layout the AllAppsRecyclerView
+        View view = launcher.findViewById(R.id.apps_list_view);
+        int paddingLeftRight = hasVerticalBarLayout ? 0 : edgeMarginPx;
+        view.setPadding(paddingLeftRight, view.getPaddingTop(), paddingLeftRight,
+                view.getPaddingBottom());
+
         if (notifyListeners) {
             for (int i = mListeners.size() - 1; i >= 0; i--) {
                 mListeners.get(i).onLauncherLayoutChanged();
@@ -656,9 +683,8 @@
         }
 
         // 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 shouldIgnoreLongPressToOverview(float touchX) {
@@ -667,4 +693,11 @@
         boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
         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/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/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..af3abeb 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() {
@@ -149,7 +151,6 @@
                 mLauncher.setAllAppsButton(allAppsButton);
                 allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
                 allAppsButton.setOnClickListener(mLauncher);
-                allAppsButton.setOnLongClickListener(mLauncher);
                 allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
             }
 
@@ -179,8 +180,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..383e6ef 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;
@@ -55,7 +51,6 @@
 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;
@@ -100,24 +95,14 @@
     @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 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();
@@ -125,16 +110,11 @@
         mLauncherApps = LauncherAppsCompat.getInstance(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.
@@ -190,7 +170,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) {
@@ -387,7 +371,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);
@@ -637,7 +621,7 @@
                     // 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);
+                    Bitmap lowResIcon =  generateLowResIcon(icon);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
                     entry.icon = useLowResIcon ? lowResIcon : icon;
@@ -769,7 +753,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 13;
+        private final static int DB_VERSION = 16;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -822,24 +806,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..6872515 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -24,8 +24,11 @@
         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..f088d11 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,6 +65,11 @@
 
     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) {
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..d7bebd1 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,8 +85,6 @@
      * Number of icons inside the hotseat area.
      */
     public int numHotseatIcons;
-    float hotseatIconSize;
-    public float hotseatScale;
     int defaultLayoutId;
 
     public DeviceProfile landscapeProfile;
@@ -100,12 +98,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.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
                 p.defaultLayoutId);
     }
 
     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) {
         name = n;
         minWidthDps = w;
         minHeightDps = h;
@@ -115,12 +113,10 @@
         numFolderColumns = fc;
         minAllAppsPredictionColumns = maapc;
         iconSize = is;
+        landscapeIconSize = lis;
         iconTextSize = its;
         numHotseatIcons = hs;
-        hotseatIconSize = his;
         defaultLayoutId = dlId;
-
-        hotseatScale = hotseatIconSize / iconSize;
     }
 
     @TargetApi(23)
@@ -153,17 +149,15 @@
         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,9 +205,9 @@
                             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.recycle();
                 }
@@ -305,19 +299,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..11c5309 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;
 
@@ -178,15 +182,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..26c5c9d 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,26 @@
 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.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;
@@ -123,7 +125,9 @@
 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;
@@ -147,7 +151,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 +162,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;
 
@@ -209,22 +215,23 @@
     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;
@@ -254,19 +261,19 @@
     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 final Handler mHandler = new Handler();
     private boolean mIsResumeFromActionScreenOff;
     private boolean mHasFocus = false;
-    private boolean mAttached = false;
+
+    private ObjectAnimator mScrimAnimator;
 
     private PopupDataProvider mPopupDataProvider;
 
@@ -276,7 +283,7 @@
     // 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 +296,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 +310,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 +366,10 @@
             mLauncherCallbacks.preOnCreate();
         }
 
+        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+        wallpaperColorInfo.setOnThemeChangeListener(this);
+        overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
+
         super.onCreate(savedInstanceState);
 
         LauncherAppState app = LauncherAppState.getInstance(this);
@@ -393,11 +404,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);
@@ -456,6 +466,25 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onCreate(savedInstanceState);
         }
+
+        // Listen for broadcasts screen off
+        registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+    }
+
+    @Override
+    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
@@ -466,6 +495,11 @@
     @Override
     public void onExtractedColorsChanged() {
         loadExtractedColorsAndColorItems();
+        mExtractedColors.notifyChange();
+    }
+
+    public ExtractedColors getExtractedColors() {
+        return mExtractedColors;
     }
 
     @Override
@@ -481,46 +515,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 +544,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;
     }
 
@@ -952,6 +905,24 @@
         if (!isWorkspaceLoading()) {
             NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
         }
+
+        if (mIsResumeFromActionScreenOff && 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();
+        }
     }
 
     @Override
@@ -977,8 +948,7 @@
             // 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);
         }
@@ -1051,13 +1021,12 @@
         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();
@@ -1072,7 +1041,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 +1062,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 +1079,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 {
@@ -1297,9 +1254,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,16 +1284,11 @@
         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);
@@ -1429,34 +1379,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);
+            info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+                    this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
         }
 
         if (info == null) {
@@ -1475,36 +1424,55 @@
             }
         }
 
-        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.
+            final long folderIconId = container;
+            FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
+                @Override
+                public boolean evaluate(ItemInfo info, View view) {
+                    return info != null && info.id == folderIconId;
+                }
+            });
 
-        if (!foundCellSpan) {
-            mWorkspace.onNoCellFound(layout);
-            return;
+            if (folderIcon != null) {
+                FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
+                folderInfo.add(info, args.rank, false);
+            } else {
+                Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
+            }
         }
-
-        getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
-        mWorkspace.addInScreen(view, info);
     }
 
     /**
@@ -1592,13 +1560,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 +1569,6 @@
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mAttached) {
-            unregisterReceiver(mReceiver);
-            mAttached = false;
-        }
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onDetachedFromWindow();
@@ -1648,7 +1606,6 @@
                                 }
                             }
                         });
-                        return;
                     }
                 });
             }
@@ -1672,10 +1629,6 @@
         return mWorkspace;
     }
 
-    public View getQsbContainer() {
-        return mQsbContainer;
-    }
-
     public Hotseat getHotseat() {
         return mHotseat;
     }
@@ -1767,7 +1720,7 @@
 
             // Reset the apps view
             if (!alreadyOnHome && mAppsView != null) {
-                mAppsView.scrollToTop();
+                mAppsView.reset();
             }
 
             // Reset the widgets view
@@ -1789,8 +1742,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 +1805,7 @@
     public void onDestroy() {
         super.onDestroy();
 
+        unregisterReceiver(mReceiver);
         mWorkspace.removeCallbacks(mBuildLayersRunnable);
         mWorkspace.removeFolderListeners();
 
@@ -1878,6 +1833,8 @@
         ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
                 .removeAccessibilityStateChangeListener(this);
 
+        WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
+
         LauncherAnimUtils.onDestroyActivity();
 
         if (mLauncherCallbacks != null) {
@@ -2361,18 +2318,7 @@
         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 */);
         }
     }
 
@@ -2469,7 +2415,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 +2482,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);
         }
 
@@ -2673,9 +2625,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 +2706,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 +2905,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 +2921,7 @@
         if (resetPageToZero) {
             mWidgetsView.scrollToTop();
         }
-        showAppsOrWidgets(State.WIDGETS, animated, false);
+        showAppsOrWidgets(State.WIDGETS, animated);
 
         mWidgetsView.post(new Runnable() {
             @Override
@@ -2994,7 +2938,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 +2956,7 @@
         }
 
         if (toState == State.APPS) {
-            mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+            mStateTransitionAnimation.startAnimationToAllApps(animated);
         } else {
             mStateTransitionAnimation.startAnimationToWidgets(animated);
         }
@@ -3085,8 +3029,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) {
@@ -3252,7 +3195,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();
@@ -3339,7 +3282,7 @@
 
         // 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;
@@ -3397,7 +3340,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);
@@ -3600,6 +3543,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 +3624,8 @@
             mPendingActivityResult = null;
         }
 
-        InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+        InstallShortcutReceiver.disableAndFlushInstallQueue(
+                InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
 
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
@@ -3719,7 +3666,7 @@
      * multiple calls to bind the same list.)
      */
     @Thunk ArrayList<AppInfo> mTmpAppsList;
-    private Runnable mBindAllApplicationsRunnable = new Runnable() {
+    private final Runnable mBindAllApplicationsRunnable = new Runnable() {
         public void run() {
             bindAllApplications(mTmpAppsList);
             mTmpAppsList = null;
@@ -3775,6 +3722,22 @@
     }
 
     @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);
+        }
+    }
+
+    @Override
     public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
         Runnable r = new Runnable() {
             public void run() {
@@ -3907,7 +3870,7 @@
         }
     }
 
-    private Runnable mBindAllWidgetsRunnable = new Runnable() {
+    private final Runnable mBindAllWidgetsRunnable = new Runnable() {
             public void run() {
                 bindAllWidgets(mAllWidgets);
             }
@@ -3947,7 +3910,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,7 +3941,7 @@
     }
 
     private boolean shouldShowDiscoveryBounce() {
-        if (mState != mState.WORKSPACE) {
+        if (mState != State.WORKSPACE) {
             return false;
         }
         if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
@@ -3987,10 +3950,7 @@
         if (!mIsResumeFromActionScreenOff) {
             return false;
         }
-        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
-            return false;
-        }
-        return true;
+        return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
     }
 
     protected void moveWorkspaceToDefaultScreen() {
@@ -4079,7 +4039,7 @@
             switch (keyCode) {
                 case KeyEvent.KEYCODE_A:
                     if (mState == State.WORKSPACE) {
-                        showAppsView(true, true, false);
+                        showAppsView(true, true);
                         return true;
                     }
                     break;
@@ -4126,9 +4086,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..cf20feb 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -26,7 +26,7 @@
 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.util.ConfigMonitor;
 import com.android.launcher3.util.Preconditions;
@@ -37,7 +37,7 @@
 
 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;
@@ -93,9 +93,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);
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 13cc7ba..c7b7782 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;
@@ -98,13 +93,7 @@
         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);
-            }
+            setExecutor(Utilities.THREAD_POOL_EXECUTOR);
         }
     }
 
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..d66b14c 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -17,13 +17,10 @@
 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 java.io.FileDescriptor;
@@ -44,69 +41,60 @@
      * 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<ComponentKey> 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();
+    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();
+    boolean shouldShowDiscoveryBounce();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b2f6284..82bee0e 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,40 @@
 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.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 +67,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 +80,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 +108,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,12 +119,19 @@
      */
     static final BgDataModel sBgDataModel = new BgDataModel();
 
-    // </ only access in worker thread >
-
-    private final IconCache mIconCache;
-
-    private final LauncherAppsCompat mLauncherApps;
-    private final UserManagerCompat mUserManager;
+    // 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();
+                }
+            }
+        }
+    };
 
     public interface Callbacks {
         public boolean setLoadOnResume();
@@ -194,6 +150,7 @@
                                   ArrayList<ItemInfo> addAnimated,
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
+        public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
         public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
                 ArrayList<ShortcutInfo> removed, UserHandle user);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
@@ -210,25 +167,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 +199,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 +313,6 @@
     public void initialize(Callbacks callbacks) {
         synchronized (mLock) {
             Preconditions.assertUIThread();
-            // Remove any queued UI runnables
-            mHandler.cancelAll();
             mCallbacks = new WeakReference<>(callbacks);
         }
     }
@@ -493,7 +424,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 +450,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 +460,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 +527,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.bindAppsAdded(null, null, null, 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 +606,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 +623,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 +648,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 +659,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,23 +685,6 @@
     }
 
     /**
-     * @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() {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 3150d5b..4813571 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;
     }
@@ -714,50 +700,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 +740,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 +830,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 +856,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 +897,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 +928,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 +938,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 +961,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 +991,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 +1005,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;
         }
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..e7349f0 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;
 
@@ -127,12 +127,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,9 +152,6 @@
             @Override
             void onTransitionComplete() {
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
-                if (startSearchAfterTransition) {
-                    toView.startAppsSearch();
-                }
             }
         };
         int animType = CIRCULAR_REVEAL;
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..255677a 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.LauncherEdgeEffect;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -226,11 +227,10 @@
         mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
         setOnHierarchyChangeListener(this);
         setWillNotDraw(false);
-    }
 
-    protected void setEdgeGlowColor(int color) {
-        mEdgeGlowLeft.setColor(color);
-        mEdgeGlowRight.setColor(color);
+        int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect);
+        mEdgeGlowLeft.setColor(edgeEffectColor);
+        mEdgeGlowRight.setColor(edgeEffectColor);
     }
 
     protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -1599,7 +1599,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,6 +1705,10 @@
         return true;
     }
 
+    protected boolean shouldFlingForVelocity(int velocityX) {
+        return Math.abs(velocityX) > mFlingThresholdVelocity;
+    }
+
     private void resetTouchState() {
         releaseVelocityTracker();
         endReordering();
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index b163464..de424ab 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
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..8caba75 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -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;
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 0902b20..b7b75f8 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -26,7 +26,6 @@
 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;
 
@@ -85,7 +84,7 @@
             }
 
             Preference iconBadgingPref = findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!BuildCompat.isAtLeastO()) {
+            if (!Utilities.isAtLeastO()) {
                 getPreferenceScreen().removePreference(
                         findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
                 getPreferenceScreen().removePreference(iconBadgingPref);
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index f8742f8..fd708c0 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -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..f0d9367 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -45,10 +45,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;
@@ -185,7 +185,7 @@
 
 
     public final boolean isPromise() {
-        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
+        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
     }
 
     public int getInstallProgress() {
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0fac29f..45c14d6 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -28,10 +28,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
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b5c44bb..9100fe2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,6 +28,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -37,7 +38,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 +51,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;
@@ -85,7 +85,7 @@
     private static final Matrix sInverseMatrix = new Matrix();
 
     public static boolean isAtLeastO() {
-        return BuildCompat.isAtLeastO();
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
     }
 
     public static final boolean ATLEAST_NOUGAT_MR1 =
@@ -110,6 +110,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 +261,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 +551,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 +577,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 +653,28 @@
         hashSet.add(elem);
         return hashSet;
     }
+
+    /**
+     * @return creates a new alpha mask bitmap out of an existing bitmap
+     */
+    public static Bitmap convertToAlphaMask(Bitmap b, int applyAlpha) {
+        Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ALPHA_8);
+        Canvas c = new Canvas(a);
+        Paint paint = new Paint();
+        paint.setAlpha(applyAlpha);
+        c.drawBitmap(b, 0f, 0f, paint);
+        return a;
+    }
+
+    /**
+     * @return a new white 1x1 bitmap with ALPHA_8
+     */
+    public static Bitmap createOnePixBitmap() {
+        Bitmap a = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+        Canvas c = new Canvas(a);
+        Paint paint = new Paint();
+        paint.setColor(Color.WHITE);
+        c.drawPaint(paint);
+        return a;
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index c525cd4..ad1be7e 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 = 8;
 
         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..767e332 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,9 +51,7 @@
 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.UninstallDropTarget.DropTargetSource;
@@ -64,7 +63,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,6 +70,7 @@
 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;
@@ -80,14 +79,12 @@
 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;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
-
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
@@ -111,7 +108,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 +134,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 +171,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 +180,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 +223,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 +241,7 @@
     private boolean mStripScreensOnPageStopMoving = false;
 
     private DragPreviewProvider mOutlineProvider = null;
-    private boolean mWorkspaceFadeInAdjacentScreens;
+    private final boolean mWorkspaceFadeInAdjacentScreens;
 
     final WallpaperOffsetInterpolator mWallpaperOffset;
     private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -265,7 +254,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 +286,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;
@@ -316,14 +305,11 @@
     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 +364,6 @@
         }
     }
 
-    public void setOnStateChangeListener(OnStateChangeListener listener) {
-        mOnStateChangeListener = listener;
-    }
-
     /**
      * Estimates the size of an item using spans: hSpan, vSpan.
      *
@@ -451,7 +433,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 +486,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 +512,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 +557,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 +598,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.qsb_container,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.qsb_container, 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 +622,7 @@
         }
 
         // Recycle the QSB widget
-        View qsb = findViewById(getEmbeddedQsbId());
+        View qsb = findViewById(R.id.qsb_container);
         if (qsb != null) {
             ((ViewGroup) qsb.getParent()).removeView(qsb);
         }
@@ -709,7 +660,7 @@
 
         // 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);
@@ -727,7 +678,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 +805,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 +907,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 +918,7 @@
         mScreenOrder.add(newId);
 
         // Update the model for the new screen
-        mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         return newId;
     }
@@ -1013,7 +963,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 +1007,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 +1032,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,
@@ -1407,17 +1357,9 @@
         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,6 +1407,13 @@
         }
     }
 
+    @Override
+    protected boolean shouldFlingForVelocity(int velocityX) {
+        // When the overlay is moving, the fling or settle transition is controlled by the overlay.
+        return Float.compare(mOverlayTranslation, 0) == 0 &&
+                super.shouldFlingForVelocity(velocityX);
+    }
+
     private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
 
     /**
@@ -1491,9 +1440,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 +1449,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);
     }
 
     /**
@@ -1701,10 +1644,6 @@
                     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);
-                    }
                 }
             }
         }
@@ -1798,34 +1737,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();
     }
 
@@ -1985,7 +1896,7 @@
             mScreenOrder.add(getIdForScreen(cl));
         }
         mLauncher.getUserEventDispatcher().logOverviewReorder();
-        mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         // Re-enable auto layout transitions for page deletion.
         enableLayoutTransitions();
@@ -2064,10 +1975,6 @@
             mLauncher.notifyWidgetProvidersChanged();
         }
 
-        if (mOnStateChangeListener != null) {
-            mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
-        }
-
         onPrepareStateTransition(mState.hasMultipleVisiblePages);
 
         StateTransitionListener listener = new StateTransitionListener();
@@ -2175,27 +2082,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 +2205,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,7 +2361,7 @@
                 // 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 {
@@ -2620,7 +2509,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 +2842,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 +3038,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 +3065,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 +3160,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 +3226,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 +3250,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 +3496,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 +3522,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 +3651,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 +3793,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 +3835,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 +3851,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);
@@ -4189,8 +4075,8 @@
 
         private boolean mRefreshPending;
 
-        public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
-                LauncherAppWidgetHost host) {
+        DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
+            LauncherAppWidgetHost host) {
             mInfos = infos;
             mHost = host;
             mHandler = new Handler();
@@ -4227,20 +4113,6 @@
         }
     }
 
-    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;
-    }
-
     private class StateTransitionListener extends AnimatorListenerAdapter
             implements AnimatorUpdateListener {
         @Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 482a2c9..32deaf2 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -355,27 +355,10 @@
                 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);
-
         if (animated) {
             Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace,
                     new PropertyListBuilder().scale(mNewScale)
@@ -393,7 +376,6 @@
             // 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());
 
@@ -407,11 +389,9 @@
 
             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 +419,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..3433533 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;
 
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/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 2c7d156..47b68a2 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,15 +20,10 @@
 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.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 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 +37,22 @@
 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.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);
     }
@@ -148,7 +137,7 @@
      */
     public void addApps(List<AppInfo> apps) {
         mApps.addApps(apps);
-        mSearchBarController.refreshSearchResult();
+        mSearchUiManager.refreshSearchResult();
     }
 
     /**
@@ -156,7 +145,18 @@
      */
     public void updateApps(List<AppInfo> apps) {
         mApps.updateApps(apps);
-        mSearchBarController.refreshSearchResult();
+        mSearchUiManager.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 +164,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 +196,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,28 +219,21 @@
             }
         });
 
-        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.setApps(mApps);
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
-        mAppsRecyclerView.addOnScrollListener(mElevationController);
-        mAppsRecyclerView.setElevationController(mElevationController);
+        if (FeatureFlags.LAUNCHER3_PHYSICS) {
+            mAppsRecyclerView.setSpringAnimationHandler(mSpringAnimationHandler);
+            mAppsRecyclerView.addOnScrollListener(new SpringMotionOnScrollListener());
+        }
+
+        mSearchContainer = findViewById(R.id.search_container);
+        mSearchUiManager = (SearchUiManager) mSearchContainer;
+        mSearchUiManager.initialize(mApps, mAppsRecyclerView);
+
 
         FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
         mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
@@ -291,18 +247,21 @@
         }
     }
 
+    public SearchUiManager getSearchUiManager() {
+        return mSearchUiManager;
+    }
+
     @Override
     public View getTouchDelegateTargetView() {
         return mAppsRecyclerView;
     }
 
     @Override
-    public void onBoundsChanged(Rect newBounds) { }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         DeviceProfile grid = mLauncher.getDeviceProfile();
+        // Update the number of items in the grid before we measure the view
         grid.updateAppsViewNumCols();
+
         if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
             if (mNumAppsPerRow != grid.inv.numColumns ||
                     mNumPredictedAppsPerRow != grid.inv.numColumns) {
@@ -313,22 +272,11 @@
                 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;
@@ -345,31 +293,13 @@
 
     @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 +358,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());
-    }
-
     @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 +380,8 @@
             ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
             navBarBgLp.height = insets.bottom;
             navBarBg.setLayoutParams(navBarBgLp);
-            navBarBg.setVisibility(View.VISIBLE);
+            navBarBg.setVisibility(FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
+                    ? View.INVISIBLE : View.VISIBLE);
         }
     }
 
@@ -498,4 +399,39 @@
             }
         }
     }
+
+    public SpringAnimationHandler getSpringAnimationHandler() {
+        return mSpringAnimationHandler;
+    }
+
+    public class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
+
+        private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
+
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING
+                    || (dx == 0 && dy == 0)) {
+                if (mSpringAnimationHandler.isRunning()){
+                    mSpringAnimationHandler.skipToEnd();
+                }
+                return;
+            }
+
+            int first = mLayoutManager.findFirstVisibleItemPosition();
+            int last = mLayoutManager.findLastVisibleItemPosition();
+
+            // We only show the spring animation when at the top or bottom, so we wait until the
+            // first or last row is visible to ensure that all animations run in sync.
+            if ((first == 0 && dy < 0) || (last == mAdapter.getItemCount() - 1 && dy > 0)) {
+                mSpringAnimationHandler.animateToFinalPosition(0);
+            }
+        }
+
+        @Override
+        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+            super.onScrollStateChanged(recyclerView, newState);
+            mScrollState = newState;
+        }
+    }
 }
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..1054a56 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,121 @@
         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);
+            }
+
+            // 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.
+            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) {
+            int appPosition = position;
+            int numDividerViews = 1 + (numPredictedApps == 0 ? 0 : 1);
+
+            int allAppsStartAt = numDividerViews + numPredictedApps;
+            if (numDividerViews == 1 || position < allAppsStartAt) {
+                appPosition -= 1;
+            } else {
+                // We cannot assume that the predicted row will always be full.
+                int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+                appPosition = position + numPredictedAppsOffset - numDividerViews;
+            }
+
+            return appPosition;
+        }
+
+        /**
+         * 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..2b2fddc 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -28,8 +28,8 @@
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
 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.userevent.nano.LauncherLogProto.ContainerType;
@@ -53,7 +53,7 @@
     private AllAppsBackgroundDrawable mEmptySearchBackground;
     private int mEmptySearchBackgroundTopOffset;
 
-    private HeaderElevationController mElevationController;
+    private SpringAnimationHandler mSpringAnimationHandler;
 
     public AllAppsRecyclerView(Context context) {
         this(context, null);
@@ -72,11 +72,22 @@
         super(context, attrs, defStyleAttr);
         Resources res = getResources();
         addOnItemTouchListener(this);
-        mScrollbar.setDetachThumbOnFastScroll();
         mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
                 R.dimen.all_apps_empty_search_bg_top_offset);
     }
 
+    public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
+        mSpringAnimationHandler = springAnimationHandler;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
+            mSpringAnimationHandler.addMovement(e);
+        }
+        return super.onTouchEvent(e);
+    }
+
     /**
      * Sets the list of apps in this view, used to determine the fastscroll position.
      */
@@ -85,8 +96,8 @@
         mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
     }
 
-    public void setElevationController(HeaderElevationController elevationController) {
-        mElevationController = elevationController;
+    public AlphabeticalAppsList getApps() {
+        return mApps;
     }
 
     /**
@@ -98,7 +109,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 +135,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 +160,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
@@ -349,7 +354,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 +374,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 +405,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 +414,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();
     }
 
     /**
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..4d112c6 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -22,8 +22,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.graphics.ScrimView;
 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;
 
@@ -38,12 +43,13 @@
  * closer to top or closer to the page indicator.
  */
 public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener,
-        View.OnLayoutChangeListener {
+         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
@@ -65,6 +71,7 @@
     private final Launcher mLauncher;
     private final VerticalPullDetector 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,10 +94,15 @@
 
     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 GradientView mGradientView;
+    private ScrimView mScrimView;
+
+    private SpringAnimationHandler mSpringAnimationHandler;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
@@ -101,12 +113,14 @@
 
         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() &&
@@ -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,12 @@
                     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.animateToFinalPosition(0);
+                }
             } else {
                 calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
                 mLauncher.showWorkspace(true);
@@ -221,11 +245,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 +269,45 @@
             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 = 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);
+
+        // scrim
+        if (mScrimView == null) {
+            mScrimView = (ScrimView) mLauncher.findViewById(R.id.scrim_bg);
+            mScrimView.setVisibility(View.VISIBLE);
+        }
+        mScrimView.setProgress(progress);
     }
 
     /**
@@ -273,31 +320,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,
@@ -451,6 +504,9 @@
 
     public void finishPullUp() {
         mHotseat.setVisibility(View.INVISIBLE);
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.reset();
+        }
         setProgress(0f);
     }
 
@@ -459,6 +515,9 @@
         mHotseat.setBackgroundTransparent(false /* transparent */);
         mHotseat.setVisibility(View.VISIBLE);
         mAppsView.reset();
+        if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.reset();
+        }
         setProgress(1f);
     }
 
@@ -486,21 +545,20 @@
         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();
+    }
+
+    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..608e898 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -24,7 +24,7 @@
 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;
@@ -138,13 +138,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;
@@ -226,6 +219,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() {
@@ -429,9 +429,6 @@
             }
         }
 
-        // Add the search divider
-        mAdapterItems.add(AdapterItem.asSearchDivider(position++));
-
         // Process the predicted app components
         mPredictedApps.clear();
         if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
@@ -440,7 +437,7 @@
                 if (info != null) {
                     mPredictedApps.add(info);
                 } else {
-                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                    if (FeatureFlags.IS_DOGFOOD_BUILD) {
                         Log.e(TAG, "Predicted app not found: " + ck);
                     }
                 }
@@ -606,6 +603,10 @@
         return result;
     }
 
+    public AppInfo findApp(ComponentKey key) {
+        return mComponentToAppMap.get(key);
+    }
+
     /**
      * 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..c0d7850
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.view.KeyEvent;
+
+/**
+ * Interface for controlling the Apps search UI.
+ */
+public interface SearchUiManager {
+
+    /**
+     * Initializes the search manager.
+     */
+    void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
+
+    /**
+     * 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
index 7800c01..13c4f63 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -17,7 +17,7 @@
     private static final boolean DBG = false;
     private static final String TAG = "VerticalPullDetector";
 
-    private float mTouchSlop;
+    private final float mTouchSlop;
 
     private int mScrollConditions;
     public static final int DIRECTION_UP = 1 << 0;
@@ -47,8 +47,6 @@
         SETTLING       // onDragEnd
     }
 
-    ;
-
     //------------------- ScrollState transition diagram -----------------------------------
     //
     // IDLE ->      (mDisplacement > mTouchSlop) -> DRAGGING
@@ -110,7 +108,7 @@
     private boolean mIgnoreSlopWhenSettling;
 
     /* Client of this gesture detector can register a callback. */
-    Listener mListener;
+    private Listener mListener;
 
     public void setListener(Listener l) {
         mListener = l;
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..5cb12d5
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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;
+
+    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);
+    }
+
+    @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 (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                !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 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);
+                    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 92%
rename from src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
rename to src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 136d369..4303302 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
 
 import android.os.Handler;
 
@@ -26,7 +26,7 @@
 /**
  * The default search implementation.
  */
-public class DefaultAppSearchAlgorithm {
+public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
 
     private final List<AppInfo> mApps;
     protected final Handler mResultHandler;
@@ -36,12 +36,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,7 +56,7 @@
         });
     }
 
-    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();
@@ -67,7 +69,7 @@
         return result;
     }
 
-    protected boolean matches(AppInfo info, String query) {
+    public static boolean matches(AppInfo info, String query) {
         int queryLength = query.length();
 
         String title = info.title.toString();
@@ -103,7 +105,7 @@
      *      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 (thisType) {
             case Character.UPPERCASE_LETTER:
                 if (nextType == Character.UPPERCASE_LETTER) {
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..7c5fa1c
--- /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 true;
+    }
+
+    @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..038f826
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.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.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 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);
+        spring.setStartVelocity(mCurrentVelocity);
+        mAnimations.add(spring);
+    }
+
+    /**
+     * Stops and removes the spring attached to {@param view}.
+     */
+    public void remove(View view) {
+        SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+        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) {
+        if (DEBUG) Log.d(TAG, "animateToFinalPosition#computeVelocity=" + mShouldComputeVelocity);
+
+        if (mShouldComputeVelocity) {
+            computeVelocity();
+            setStartVelocity(mCurrentVelocity);
+        }
+
+        int size = mAnimations.size();
+        for (int i = 0; i < size; ++i) {
+            mAnimations.get(i).animateToFinalPosition(position);
+        }
+
+        reset();
+    }
+
+    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;
+    }
+
+    private void setStartVelocity(float velocity) {
+        int size = mAnimations.size();
+        for (int i = 0; i < size; ++i) {
+            mAnimations.get(i).setStartVelocity(velocity);
+        }
+    }
+
+    private void computeVelocity() {
+        getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
+
+        mCurrentVelocity = isVerticalDirection()
+                ? getVelocityTracker().getYVelocity()
+                : getVelocityTracker().getXVelocity();
+        mCurrentVelocity *= VELOCITY_DAMPING_FACTOR;
+        mShouldComputeVelocity = false;
+
+        if (DEBUG) Log.d(TAG, "computeVelocity=" + mCurrentVelocity);
+    }
+
+    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);
+    }
+
+    /**
+     * 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.setStartValue(1f);
+        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/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index e997a99..75a2a5d 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,7 +48,7 @@
     }
 
     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) {
@@ -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..58d2a80
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.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.compat;
+
+/**
+ * A compatibility layer around platform implementation of WallpaperColors
+ */
+public class WallpaperColorsCompat {
+
+    public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
+
+    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..cbcabdf
--- /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.isAtLeastO()) {
+                    try {
+                        sInstance = new WallpaperManagerCompatVOMR1(context);
+                    } catch (Exception 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..6233fab
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.WallpaperManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
+
+    private static final String TAG = "WMCompatVOMR1";
+
+    private final WallpaperManager mWm;
+
+    private final Class mOCLClass;
+    private final Method mAddOCLMethod;
+
+    private final Method mWCGetMethod;
+    private final Method mWCGetPrimaryColorMethod;
+    private final Method mWCGetSecondaryColorMethod;
+    private final Method mWCGetTertiaryColorMethod;
+    private final Method mWCColorHintsMethod;
+
+    WallpaperManagerCompatVOMR1(Context context) throws Exception {
+        mWm = context.getSystemService(WallpaperManager.class);
+
+        mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener");
+        mAddOCLMethod = WallpaperManager.class.getDeclaredMethod(
+                "addOnColorsChangedListener", mOCLClass);
+
+        mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
+        Class wallpaperColorsClass = mWCGetMethod.getReturnType();
+        mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor");
+        mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor");
+        mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor");
+        mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints");
+    }
+
+    @Nullable
+    @Override
+    public WallpaperColorsCompat getWallpaperColors(int which) {
+        try {
+            return convertColorsObject(mWCGetMethod.invoke(mWm, which));
+        } catch (Exception e) {
+            Log.e(TAG, "Error calling wallpaper API", e);
+            return null;
+        }
+    }
+
+    @Override
+    public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
+        Object onChangeListener = Proxy.newProxyInstance(
+                WallpaperManager.class.getClassLoader(),
+                new Class[]{mOCLClass},
+                new InvocationHandler() {
+                    @Override
+                    public Object invoke(Object o, Method method, Object[] objects)
+                            throws Throwable {
+                        String methodName = method.getName();
+                        if ("onColorsChanged".equals(methodName)) {
+                            listener.onColorsChanged(
+                                    convertColorsObject(objects[0]), (Integer) objects[1]);
+                        } else if ("toString".equals(methodName)) {
+                            return listener.toString();
+                        }
+                        return null;
+                    }
+                });
+        try {
+            mAddOCLMethod.invoke(mWm, onChangeListener);
+        } catch (Exception e) {
+            Log.e(TAG, "Error calling wallpaper API", e);
+        }
+    }
+
+    private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception {
+        if (colors == null) {
+            return null;
+        }
+        Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors);
+        Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors);
+        Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors);
+        int primaryVal = primary != null ? primary.toArgb() : 0;
+        int secondaryVal = secondary != null ? secondary.toArgb() : 0;
+        int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
+        int colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+        return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
+    }
+}
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..01893bd 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -28,6 +28,7 @@
 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;
@@ -50,7 +51,7 @@
 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 +61,7 @@
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetImageView;
 
-@TargetApi(Build.VERSION_CODES.N_MR1)
+@TargetApi(Build.VERSION_CODES.O)
 public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
 
     private static final int SHADOW_SIZE = 10;
@@ -70,7 +71,7 @@
 
     private final PointF mLastTouchPos = new PointF();
 
-    private PinItemRequestCompat mRequest;
+    private PinItemRequest mRequest;
     private LauncherAppState mApp;
     private InvariantDeviceProfile mIdp;
 
@@ -83,11 +84,13 @@
     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 +104,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 +165,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 +185,14 @@
         return false;
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mFinishOnPause) {
+            finish();
+        }
+    }
+
     private void setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
@@ -226,7 +238,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);
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..be5f01a 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,
@@ -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
@@ -876,7 +879,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..022b3b8 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,31 +16,67 @@
 
 package com.android.launcher3.dragndrop;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 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.ColorFilter;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 
+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 {
+public class DragView extends FrameLayout {
     public static final int COLOR_CHANGE_DURATION = 120;
     public static final int VIEW_ZOOM_DURATION = 150;
 
@@ -57,6 +93,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 +113,18 @@
     private int mAnimatedShiftX;
     private int mAnimatedShiftY;
 
+    // Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true}
+    private SpringAnimation mSpringX, mSpringY;
+    private ImageView mFgImageView, mBgImageView;
+    private Path mScaledMaskPath;
+    private Drawable mBadge;
+
+    // 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 int mDelta;
+
     /**
      * Construct the drag view.
      * <p>
@@ -89,6 +138,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,8 +192,210 @@
         mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
 
         mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
         setElevation(getResources().getDimension(R.dimen.drag_elevation));
+        setWillNotDraw(false);
+        mDelta = (int)(getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP);
+    }
+
+    /**
+     * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
+     */
+    @TargetApi(Build.VERSION_CODES.O)
+    public void setItemInfo(final ItemInfo info) {
+        if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
+            return;
+        }
+        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
+                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            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];
+                Drawable dr = getFullDrawable(info, appState, outObj);
+
+                if (dr instanceof AdaptiveIconDrawable) {
+                    int w = mBitmap.getWidth();
+                    int h = mBitmap.getHeight();
+                    AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
+                    adaptiveIcon.setBounds(0, 0, w, h);
+                    float blurSizeOutline = mLauncher.getResources()
+                            .getDimension(R.dimen.blur_size_medium_outline);
+                    float normalizationScale = IconNormalizer.getInstance(mLauncher)
+                            .getScale(adaptiveIcon, null, null, null) * ((w - blurSizeOutline) / w);
+
+                    final Path mask = getMaskPath(adaptiveIcon, normalizationScale);
+                    mFgImageView = setupImageView(adaptiveIcon.getForeground(), normalizationScale);
+                    mBgImageView = setupImageView(adaptiveIcon.getBackground(), normalizationScale);
+                    mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X);
+                    mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y);
+
+                    mBadge = getBadge(info, appState, outObj[0]);
+                    int blurMargin = (int) blurSizeOutline / 2;
+                    mBadge.setBounds(blurMargin, blurMargin, w - blurMargin, h - blurMargin);
+
+                    new Handler(Looper.getMainLooper()).post(new Runnable() {
+                        @Override
+                        public void run() {
+                            // Assign the variable on the UI thread to avoid race conditions.
+                            mScaledMaskPath = mask;
+                            addView(mBgImageView);
+                            addView(mFgImageView);
+                            setWillNotDraw(true);
+
+                            if (info.isDisabled()) {
+                                FastBitmapDrawable d = new FastBitmapDrawable(null);
+                                d.setIsDisabled(true);
+                                ColorFilter cf = d.getColorFilter();
+                                mBgImageView.setColorFilter(cf);
+                                mFgImageView.setColorFilter(cf);
+                                mBadge.setColorFilter(cf);
+                            }
+                        }
+                    });
+                }
+            }});
+    }
+
+    /**
+     * 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 {
+            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 {
+            return mLauncher.getPackageManager()
+                    .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
+        }
+    }
+
+    private ImageView setupImageView(Drawable drawable, float normalizationScale) {
+        FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
+        ImageView imageViewOut = new ImageView(getContext());
+        imageViewOut.setLayoutParams(params);
+        imageViewOut.setScaleType(ImageView.ScaleType.FIT_XY);
+        imageViewOut.setScaleX(normalizationScale);
+        imageViewOut.setScaleY(normalizationScale);
+        imageViewOut.setImageDrawable(drawable);
+        return imageViewOut;
+    }
+
+    private SpringAnimation setupSpringAnimation(int minValue, int maxValue,
+            DynamicAnimation.ViewProperty property) {
+        SpringAnimation s = new SpringAnimation(mFgImageView, property, 0);
+        s.setMinValue(minValue).setMaxValue(maxValue);
+        s.setSpring(new SpringForce(0)
+                        .setDampingRatio(DAMPENING_RATIO)
+                        .setStiffness(STIFFNESS));
+        return s;
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    private Path getMaskPath(AdaptiveIconDrawable dr, float normalizationScale) {
+        Matrix m = new Matrix();
+        // Shrink very tiny bit so that the clip path is smaller than the original bitmap
+        // that has anti aliased edges and shadows.
+        float s = normalizationScale * .97f;
+        m.setScale(s, s, dr.getBounds().centerX(), dr.getBounds().centerY());
+        Path p = new Path();
+        dr.getIconMask().transform(m, p);
+        return p;
+    }
+
+    private void applySpring(int x, int y) {
+        if (mSpringX == null || mSpringY == null) {
+            return;
+        }
+        mSpringX.animateToFinalPosition(Utilities.boundToRange(x, -mDelta, mDelta));
+        mSpringY.animateToFinalPosition(Utilities.boundToRange(y, -mDelta, mDelta));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        int w = right - left;
+        int h = bottom - top;
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).layout(-w / 4, -h / 4, w + w / 4, h + h / 4);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int w = mBitmap.getWidth();
+        int h = mBitmap.getHeight();
+        setMeasuredDimension(w, h);
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).measure(w, h);
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mScaledMaskPath != null) {
+            int cnt = canvas.save();
+            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+            canvas.clipPath(mScaledMaskPath);
+            super.dispatchDraw(canvas);
+            canvas.restoreToCount(cnt);
+            mBadge.draw(canvas);
+        } else {
+            super.dispatchDraw(canvas);
+        }
     }
 
     /** Sets the scale of the view over the normal workspace icon size. */
@@ -187,11 +439,6 @@
         return mDragRegion;
     }
 
-    @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) {
@@ -343,6 +590,9 @@
      * @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) {
+            applySpring(mLastTouchX - touchX, mLastTouchY - touchY);
+        }
         mLastTouchX = touchX;
         mLastTouchY = touchY;
         applyTranslation();
@@ -391,4 +641,24 @@
     public float getInitialScale() {
         return mInitialScale;
     }
+
+    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/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..c8d3890 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.isAtLeastO()) {
+            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..52abbc7 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -21,6 +21,7 @@
 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;
@@ -30,26 +31,25 @@
 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;
@@ -80,7 +80,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..de614f0
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+    // Temporary variable to avoid allocations
+    private float[] mTmpHSL = new float[3];
+
+    public @Nullable Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
+        if (inWallpaperColors == null) {
+            return null;
+        }
+
+        final List<Integer> mainColors = getMainColors(inWallpaperColors);
+        final int mainColorsSize = mainColors.size();
+
+        if (mainColorsSize == 0) {
+            return null;
+        }
+        // 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 null;
+        }
+
+        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 null;
+        }
+
+        // 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 null;
+        }
+
+        // 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);
+
+        final int textInversionIndex = h.length - 3;
+
+        int primaryIndex;
+        int secondaryIndex;
+
+        // Dark colors:
+        // Stops at 4th color, only lighter if dark text is supported
+        if (fitIndex < 2) {
+            primaryIndex = 0;
+        } else if (fitIndex < textInversionIndex) {
+            primaryIndex = Math.min(fitIndex, 3);
+        } else {
+            primaryIndex = h.length - 1;
+        }
+        secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+
+        int mainColor = getColorInt(primaryIndex, h, s, l);
+        int secondaryColor = getColorInt(secondaryIndex, h, s, l);
+
+        return new Pair<>(mainColor, secondaryColor);
+    }
+
+    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..512e89a
--- /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;
+        float[] hsl = new float[3];
+        ColorUtils.colorToHSL(mMainColor, hsl);
+        mIsDark = hsl[2] < 0.2f;
+    }
+
+    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..ff357c0 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -1,12 +1,5 @@
 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;
@@ -27,7 +20,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,8 +29,8 @@
     }
 
     @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;
@@ -54,7 +47,7 @@
         }
 
         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;
@@ -121,6 +114,11 @@
     }
 
     @Override
+    public float getIconSize() {
+        return mIconSize;
+    }
+
+    @Override
     public int maxNumItems() {
         return MAX_NUM_ITEMS_IN_PREVIEW;
     }
@@ -129,24 +127,4 @@
     public boolean clipToBackground() {
         return true;
     }
-
-    @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;
-
-            // 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));
-    }
 }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 93c9ea8..3c7c698 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,20 @@
         }
         mDragController.removeDropTarget(this);
         clearFocus();
-        if (wasAnimated) {
-            mFolderIcon.requestFocus();
+        if (mFolderIcon != null) {
+            mFolderIcon.setVisibility(View.VISIBLE);
+            if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+                mFolderIcon.mFolderName.setTextVisibility(true);
+                mFolderIcon.setBackgroundVisible(true);
+                mFolderIcon.mBackground.fadeInBackgroundShadow();
+            }
+            if (wasAnimated) {
+                mFolderIcon.mBackground.animateBackgroundStroke();
+                if (mFolderIcon.hasBadge()) {
+                    mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
+                }
+                mFolderIcon.requestFocus();
+            }
         }
 
         if (mRearrangeOnClose) {
@@ -1027,6 +1091,9 @@
     }
 
     public boolean isDropEnabled() {
+        if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+            return mState != STATE_ANIMATING;
+        }
         return true;
     }
 
@@ -1288,53 +1355,68 @@
         }
         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);
+        if (d.dragInfo instanceof PendingAddShortcutInfo) {
+            PendingAddShortcutInfo pasi = (PendingAddShortcutInfo) d.dragInfo;
+            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 (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 +1445,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 +1513,26 @@
         return mItemsInReadingOrder;
     }
 
+    public List<BubbleTextView> getItemsOnCurrentPage() {
+        ArrayList<View> allItems = getItemsInReadingOrder();
+        int currentPage = mContent.getCurrentPage();
+        int lastPage = mContent.getPageCount() - 1;
+        int totalItemsInFolder = allItems.size();
+        int itemsPerPage = mContent.itemsPerPage();
+        int numItemsOnCurrentPage = currentPage == lastPage
+                ? totalItemsInFolder - (itemsPerPage * currentPage)
+                : itemsPerPage;
+
+        int startIndex = currentPage * itemsPerPage;
+        int endIndex = startIndex + numItemsOnCurrentPage;
+
+        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..3648c60
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.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.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 mLargeFolderPreviewItemInterpolator;
+
+    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);
+        mLargeFolderPreviewItemInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.large_folder_preview_item_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.getItemsToDisplay();
+
+        // Match position of the FolderIcon
+        final Rect folderIconPos = new Rect();
+        float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+                .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+        float initialSize = (mFolderIcon.mBackground.getRadius() * 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);
+
+        // Background can have a scaled radius in drag and drop mode.
+        int radiusDiff = mFolderIcon.mBackground.getScaledRadius()
+                - mFolderIcon.mBackground.getRadius();
+
+        int initialX = folderIconPos.left + mFolderIcon.mBackground.getOffsetX() - paddingOffsetX
+                - previewItemOffsetX + radiusDiff;
+        int initialY = folderIconPos.top + mFolderIcon.mBackground.getOffsetY() - paddingOffsetY
+                + radiusDiff;
+        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.getItemsOnCurrentPage()) {
+            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));
+        play(a, new RoundedRectRevealOutlineProvider(initialRadius, finalRadius, startRect,
+                endRect).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, midDuration, 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);
+        }
+
+        addPreviewItemAnimators(a, initialScale / scaleRelativeToDragLayer, previewItemOffsetX);
+        return a;
+    }
+
+    /**
+     * Animate the items that are displayed in the preview.
+     */
+    private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
+            int previewItemOffsetX) {
+        FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+        final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+        final int numItemsInPreview = itemsInPreview.size();
+
+        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.
+            float previewScale = rule.scaleForItem(i, numItemsInPreview);
+            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, numItemsInPreview, 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 / 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.
+                if (mIsOpening) {
+                    translationX.setStartDelay(mDelay);
+                    translationY.setStartDelay(mDelay);
+                    scaleAnimator.setStartDelay(mDelay);
+                }
+                translationX.setDuration(translationX.getDuration() - mDelay);
+                translationY.setDuration(translationY.getDuration() - mDelay);
+                scaleAnimator.setDuration(scaleAnimator.getDuration() - mDelay);
+            }
+
+            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 mLargeFolderPreviewItemInterpolator;
+        }
+        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..1cc285e 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;
@@ -56,7 +46,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;
@@ -77,6 +66,7 @@
 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;
@@ -97,8 +87,6 @@
     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;
@@ -113,11 +101,12 @@
 
     // 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 float mIntrinsicIconSize = -1;
     private int mTotalWidth = -1;
     private int mPrevTopPadding = -1;
 
     PreviewBackground mBackground = new PreviewBackground();
+    private boolean mBackgroundIsVisible = true;
 
     private PreviewLayoutRule mPreviewLayoutRule;
 
@@ -126,8 +115,9 @@
 
     private float mSlop;
 
+    FolderIconPreviewVerifier mPreviewVerifier;
     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
+    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<>();
     private Drawable mReferenceDrawable = null;
 
     private Alarm mOpenAlarm = new Alarm();
@@ -181,7 +171,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,6 +212,7 @@
 
     private void setFolder(Folder folder) {
         mFolder = folder;
+        mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
         updateItemDrawingParams(false);
     }
 
@@ -233,8 +225,7 @@
     }
 
     public boolean acceptDrop(ItemInfo dragInfo) {
-        final ItemInfo item = dragInfo;
-        return !mFolder.isDestroyed() && willAcceptItem(item);
+        return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
     public void addItem(ShortcutInfo item) {
@@ -249,11 +240,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);
         }
     }
@@ -383,14 +372,11 @@
     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());
+            mBackground.setup(mLauncher, this, mTotalWidth, getPaddingTop());
             mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
                     Utilities.isRtl(getResources()));
 
@@ -407,6 +393,10 @@
         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,44 +405,19 @@
         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) {
@@ -464,12 +429,12 @@
         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);
+        center[0] = Math.round(offsetX);
+        center[1] = Math.round(offsetY);
         return mTmpParams.scale;
     }
 
-    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+    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
@@ -496,343 +461,32 @@
         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);
+            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();
     }
 
-    /**
-     * 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();
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
+        if (!mBackgroundIsVisible) return;
+
         if (mReferenceDrawable != null) {
             computePreviewDrawingParams(mReferenceDrawable);
         }
@@ -892,89 +546,14 @@
         }
     }
 
-    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,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), -1, -1, 0, 2, duration,
                     onCompleteRunnable);
         } else {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), 0, 2, -1, -1, duration,
                     onCompleteRunnable);
         }
         anim.start();
@@ -992,8 +571,36 @@
         return mFolderName.getVisibility() == VISIBLE;
     }
 
+    public List<BubbleTextView> getItemsToDisplay() {
+        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
+
+        List<BubbleTextView> itemsToDisplay = new ArrayList<>();
+        List<View> allItems = mFolder.getItemsInReadingOrder();
+        int numItems = allItems.size();
+        for (int rank = 0; rank < numItems; ++rank) {
+            if (mPreviewVerifier.isItemInPreview(rank)) {
+                itemsToDisplay.add((BubbleTextView) allItems.get(rank));
+            }
+
+            if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+                break;
+            }
+        }
+        return itemsToDisplay;
+    }
+
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable who) {
+        for (int i = 0; i < mDrawingParams.size(); i++) {
+            if (mDrawingParams.get(i).drawable == who) {
+                return true;
+            }
+        }
+        return super.verifyDrawable(who);
+    }
+
     private void updateItemDrawingParams(boolean animate) {
-        List<View> items = mPreviewLayoutRule.getItemsToDisplay(mFolder);
+        List<BubbleTextView> items = getItemsToDisplay();
         int nItemsInPreview = items.size();
 
         int prevNumItems = mDrawingParams.size();
@@ -1008,7 +615,13 @@
 
         for (int i = 0; i < mDrawingParams.size(); i++) {
             PreviewItemDrawingParams p = mDrawingParams.get(i);
-            p.drawable = ((TextView) items.get(i)).getCompoundDrawables()[1];
+            p.drawable = items.get(i).getCompoundDrawables()[1];
+
+            if (p.drawable != null && !mFolder.isOpen()) {
+                // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+                // callback will be release when the folder is opened.
+                p.drawable.setCallback(this);
+            }
 
             if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
                 computePreviewItemDrawingParams(i, nItemsInPreview, p);
@@ -1016,7 +629,7 @@
                     mReferenceDrawable = p.drawable;
                 }
             } else {
-                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
+                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
                         nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
 
                 if (p.anim != null) {
@@ -1044,7 +657,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 +723,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 +746,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 +762,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 +790,13 @@
         }
     }
 
-    public interface PreviewLayoutRule {
+    interface PreviewLayoutRule {
         PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params);
-        void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+        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);
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
new file mode 100644
index 0000000..de962b0
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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();
+        mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+                && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
+                && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
+
+        if (mDisplayingUpperLeftQuadrant) {
+            FolderPagedView.calculateGridSize(info.contents.size(), 0, 0, mMaxGridCountX,
+                    mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+            mGridCountX = mGridSize[0];
+        }
+    }
+
+    public boolean isItemInPreview(int rank) {
+        if (mDisplayingUpperLeftQuadrant) {
+            // Returns true iff the icon is in the 2x2 upper left quadrant of the Folder.
+            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..d0ac9f4 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() {
@@ -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);
+                }
             }
         }
     }
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
new file mode 100644
index 0000000..0da7c5c
--- /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 ValueAnimator mValueAnimator;
+
+    float finalScale;
+    float finalTransX;
+    float finalTransY;
+
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+    /**
+     * @param folderIcon The FolderIcon this preview will be drawn in.
+     * @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 FolderIcon folderIcon, final PreviewItemDrawingParams params,
+            int index0, int items0, int index1, int items1, int duration,
+            final Runnable onCompleteRunnable) {
+        folderIcon.computePreviewItemDrawingParams(index1, items1, mTmpParams);
+
+        finalScale = mTmpParams.scale;
+        finalTransX = mTmpParams.transX;
+        finalTransY = mTmpParams.transY;
+
+        folderIcon.computePreviewItemDrawingParams(index0, items0, 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 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);
+                folderIcon.invalidate();
+            }
+        });
+        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..44ebbcd
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 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 void drawBackground(Canvas canvas) {
+        mPaint.setStyle(Paint.Style.FILL);
+        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
+
+        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.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);
+    }
+
+    // It is the callers responsibility to save and restore the canvas layers.
+    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.
+    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();
+    }
+
+    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/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 297203a..138dc1c 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;
@@ -98,10 +97,4 @@
     public boolean clipToBackground() {
         return false;
     }
-
-    @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));
-    }
 }
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..45344c0 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.
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..9dd9504
--- /dev/null
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+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;
+
+/**
+ * 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 float GRADIENT_ALPHA_MASK_LENGTH_DP = 300;
+    private static final boolean DEBUG = false;
+
+    private final Bitmap mFinalGradientMask;
+    private final Bitmap mAlphaGradientMask;
+
+    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 mPaint = new Paint();
+    private float mProgress;
+    private final int mMaskHeight;
+    private final Context mAppContext;
+    private final Paint mDebugPaint = DEBUG ? new Paint() : null;
+    private final Interpolator mAccelerator = new AccelerateInterpolator();
+    private final float mAlphaStart;
+    private final WallpaperColorInfo mWallpaperColorInfo;
+
+    public GradientView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        this.mAppContext = context.getApplicationContext();
+        this.mMaskHeight = Utilities.pxFromDp(GRADIENT_ALPHA_MASK_LENGTH_DP,
+                mAppContext.getResources().getDisplayMetrics());
+        Launcher launcher = Launcher.getLauncher(context);
+        this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100;
+        this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
+        updateColors();
+
+        int finalAlpha = 0xBF;
+        mFinalGradientMask = Utilities.convertToAlphaMask(
+                Utilities.createOnePixBitmap(), finalAlpha);
+        Bitmap alphaMaskFromResource = BitmapFactory.decodeResource(context.getResources(),
+                R.drawable.all_apps_alpha_mask);
+        mAlphaGradientMask = Utilities.convertToAlphaMask(
+                alphaMaskFromResource, finalAlpha);
+    }
+
+    @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 = mWallpaperColorInfo.getMainColor();
+        this.mColor2 = mWallpaperColorInfo.getSecondaryColor();
+        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 shader = new RadialGradient(
+                mWidth * 0.5f,
+                mHeight * gradientCenterY,
+                radius,
+                new int[] {mColor1, mColor1, mColor2},
+                new float[] {0f, posScreenBottom, 1f},
+                Shader.TileMode.CLAMP);
+        mPaint.setShader(shader);
+    }
+
+    public void setProgress(float progress) {
+        this.mProgress = progress;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        float head = 0.29f;
+        float linearProgress = head + (mProgress * (1f - head));
+        float startMaskY = (1f - linearProgress) * mHeight - mMaskHeight * linearProgress;
+        float interpolatedAlpha = (255 - mAlphaStart) * mAccelerator.getInterpolation(mProgress);
+        mPaint.setAlpha((int) (mAlphaStart + interpolatedAlpha));
+        mAlphaMaskRect.set(0, startMaskY, mWidth, startMaskY + mMaskHeight);
+        mFinalMaskRect.set(0, startMaskY + mMaskHeight, mWidth, mHeight);
+        canvas.drawBitmap(mAlphaGradientMask, null, mAlphaMaskRect, mPaint);
+        canvas.drawBitmap(mFinalGradientMask, null, mFinalMaskRect, mPaint);
+
+        if (DEBUG) {
+            mDebugPaint.setColor(0xFF00FF00);
+            canvas.drawLine(0, startMaskY, mWidth, startMaskY, mDebugPaint);
+            canvas.drawLine(0, startMaskY + mMaskHeight, mWidth, startMaskY + mMaskHeight, mDebugPaint);
+        }
+    }
+}
\ 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/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..654fa98 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;
 
@@ -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..830ca82 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -322,14 +322,16 @@
         IconCache cache = app.getIconCache();
         Bitmap unbadgedBitmap = unbadgedDrawable == null
                 ? cache.getDefaultIcon(Process.myUserHandle())
-                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context,
-                Build.VERSION_CODES.O);
+                : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context, 0);
 
         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 +349,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/ScrimView.java b/src/com/android/launcher3/graphics/ScrimView.java
new file mode 100644
index 0000000..6d1f30a
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ScrimView.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.graphics;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+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.util.Themes;
+
+public class ScrimView extends View {
+
+    private static final boolean DEBUG = false;
+
+    private static final int MASK_HEIGHT_DP = 300;
+    private static final float MASK_START_LENGTH_FACTOR = 1f;
+    private static final boolean APPLY_ALPHA = true;
+
+    private final Bitmap mFinalScrimMask;
+    private final Bitmap mAlphaScrimMask;
+
+    private final int mMaskHeight;
+    private int mVisibleHeight;
+    private final int mHeadStart;
+
+    private final RectF mAlphaMaskRect = new RectF();
+    private final RectF mFinalMaskRect = new RectF();
+    private final Paint mPaint = new Paint();
+    private float mProgress;
+    private final Interpolator mAccelerator = new AccelerateInterpolator();
+    private final Paint mDebugPaint = DEBUG ? new Paint() : null;
+    private final int mAlphaStart;
+
+    public ScrimView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mMaskHeight = Utilities.pxFromDp(MASK_HEIGHT_DP, getResources().getDisplayMetrics());
+        mHeadStart = (int) (mMaskHeight * MASK_START_LENGTH_FACTOR);
+        mAlphaStart = Launcher.getLauncher(context)
+                .getDeviceProfile().isVerticalBarLayout() ? 0 : 55;
+
+        int scrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+        int scrimAlpha = Color.alpha(scrimColor);
+        mPaint.setColor(scrimColor);
+        mFinalScrimMask = Utilities.convertToAlphaMask(
+                Utilities.createOnePixBitmap(), scrimAlpha);
+        Bitmap alphaMaskFromResource = BitmapFactory.decodeResource(getResources(),
+                R.drawable.all_apps_alpha_mask);
+        mAlphaScrimMask = Utilities.convertToAlphaMask(alphaMaskFromResource, scrimAlpha);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        mVisibleHeight = MeasureSpec.getSize(heightMeasureSpec);
+        setMeasuredDimension(width, mVisibleHeight * 2);
+        setProgress(mProgress);
+    }
+
+    public void setProgress(float progress) {
+        mProgress = progress;
+        float initialY = mVisibleHeight - mHeadStart;
+        float fullTranslationY = mVisibleHeight;
+        float linTranslationY = initialY - progress * fullTranslationY;
+        setTranslationY(linTranslationY);
+
+        if (APPLY_ALPHA) {
+            int alpha = mAlphaStart + (int) ((255f - mAlphaStart)
+                    * mAccelerator.getInterpolation(progress));
+            mPaint.setAlpha(alpha);
+            invalidate();
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mAlphaMaskRect.set(0, 0, getWidth(), mMaskHeight);
+        mFinalMaskRect.set(0, mMaskHeight, getWidth(), getHeight());
+        canvas.drawBitmap(mAlphaScrimMask, null, mAlphaMaskRect, mPaint);
+        canvas.drawBitmap(mFinalScrimMask, null, mFinalMaskRect, mPaint);
+
+        if (DEBUG) {
+            mDebugPaint.setColor(0xFF0000FF);
+            canvas.drawLine(0, mAlphaMaskRect.top, getWidth(), mAlphaMaskRect.top, mDebugPaint);
+            canvas.drawLine(0, mAlphaMaskRect.bottom, getWidth(), mAlphaMaskRect.bottom, mDebugPaint);
+        }
+    }
+
+}
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
new file mode 100644
index 0000000..ffcedb2
--- /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.isAtLeastO()) {
+            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..fffea8e 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}
@@ -83,48 +84,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 +115,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..4c83e9a 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;
@@ -40,7 +39,7 @@
     private static File sLogsDirectory = null;
 
     public static void setDir(File logsDir) {
-        if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
             synchronized (DATE_FORMAT) {
                 // If the target directory changes, stop any active thread.
                 if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
@@ -77,7 +76,7 @@
     }
 
     public static void print(String tag, String msg, Exception e) {
-        if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
             return;
         }
         String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
@@ -103,7 +102,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 (!FeatureFlags.IS_DOGFOOD_BUILD) {
             return;
         }
         CountDownLatch latch = new CountDownLatch(1);
@@ -136,7 +135,7 @@
 
         @Override
         public boolean handleMessage(Message msg) {
-            if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) {
+            if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) {
                 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..329b7d5 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;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 258af16..edbb88c 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.SystemClock;
+import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewParent;
@@ -29,7 +30,7 @@
 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;
@@ -60,7 +61,7 @@
 
     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);
 
     public static UserEventDispatcher newInstance(Context context, boolean isInLandscapeMode,
             boolean isInMultiWindowMode) {
@@ -90,7 +91,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();
@@ -147,7 +148,11 @@
         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.
+     */
+    private 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) {
@@ -203,9 +208,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);
     }
 
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 9696054..68012c4 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;
@@ -129,6 +162,10 @@
                 }
             });
         }
+
+        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..9b4510f
--- /dev/null
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -0,0 +1,135 @@
+/*
+ * 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.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(
+            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);
+            }
+        });
+    }
+
+    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);
+            }
+        });
+    }
+}
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..8597e10 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;
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..1a2c04d 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -219,7 +219,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..0df8b6f
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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);
+        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 = 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 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 (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, 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 = mCallbacks.get();
+                    if (callbacks != null) {
+                        callbacks.bindAppWidget(widget);
+                    }
+                }
+            };
+            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..bb2d0b6
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.os.Handler;
+import android.os.Looper;
+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 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 (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);
+                                    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, 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:
+                        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..1e0af68 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,21 +18,24 @@
 import android.content.ComponentName;
 
 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 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;
 
@@ -47,6 +50,44 @@
             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) {
@@ -56,7 +97,6 @@
                     if (si.isPromise() && (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..1b2f8d6 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,12 +18,11 @@
 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;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconCache;
@@ -31,32 +30,31 @@
 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.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,12 +93,15 @@
                 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.isAtLeastO() && !Process.myUserHandle().equals(mUser)) {
+                        SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
+                    }
                 }
                 break;
             }
@@ -115,10 +116,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);
                 }
@@ -152,7 +149,7 @@
 
         ArrayList<AppInfo> added = null;
         ArrayList<AppInfo> modified = null;
-        final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
+        final ArrayList<AppInfo> removedApps = new ArrayList<>();
 
         if (appsList.added.size() > 0) {
             added = new ArrayList<>(appsList.added);
@@ -167,7 +164,7 @@
             appsList.removed.clear();
         }
 
-        final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
+        final ArrayMap<ComponentName, AppInfo> addedOrUpdatedApps = new ArrayMap<>();
 
         if (added != null) {
             final ArrayList<AppInfo> addedApps = added;
@@ -222,18 +219,17 @@
                         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)) {
+                            // For system apps, package manager send OP_UPDATE when an
+                            // app is enabled.
+                            if (si.isPromise() && (mOp == OP_ADD || mOp == OP_UPDATE)) {
+                                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);
@@ -374,11 +370,9 @@
         } else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
             // Load widgets for the new package.
             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..6f32585 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.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.graphics.LauncherIcons;
@@ -35,7 +34,7 @@
 /**
  * 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;
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 363f1ee..5682006 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;
@@ -39,7 +38,7 @@
 /**
  * Task to handle changing of lock state of the user
  */
-public class UserLockStateChangedTask extends ExtendedModelTask {
+public class UserLockStateChangedTask extends BaseModelUpdateTask {
 
     private final UserHandle mUser;
 
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/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 051c033..b83c9b9 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -205,6 +205,7 @@
                 collapseFooter.addListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
+                        ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
                         ((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
                     }
                 });
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 1a93e11..1b7c87b 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;
@@ -105,7 +106,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..0b08ef8 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -21,7 +21,6 @@
 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;
@@ -30,11 +29,12 @@
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.PillHeightRevealOutlineProvider;
+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.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
 
 import java.util.List;
 
@@ -86,10 +86,16 @@
         return getHeight() - footerHeight;
     }
 
-    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) {
+        Rect startRect = new Rect(mPillRect);
+        Rect endRect = new Rect(mPillRect);
+        if (shouldRemoveFromTop) {
+            endRect.top += heightToRemove;
+        } else {
+            endRect.bottom -= heightToRemove;
+        }
+        return new RoundedRectRevealOutlineProvider(getBackgroundRadius(), getBackgroundRadius(),
+                startRect, endRect, mRoundedCorners).createRevealAnimator(this, false);
     }
 
     public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
@@ -98,7 +104,7 @@
             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));
             }
             mHeaderCount.setTextColor(mNotificationHeaderTextColor);
         }
@@ -163,13 +169,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..baaa66a 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,25 +16,25 @@
 
 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.Pair;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -44,6 +44,7 @@
  * 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 {
 
     private static final int MSG_NOTIFICATION_POSTED = 1;
@@ -57,9 +58,9 @@
     private final Handler mWorkerHandler;
     private final Handler mUiHandler;
 
-    private Ranking mTempRanking = new Ranking();
+    private final Ranking mTempRanking = new Ranking();
 
-    private Handler.Callback mWorkerCallback = new Handler.Callback() {
+    private final Handler.Callback mWorkerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
             switch (message.what) {
@@ -80,7 +81,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) {
@@ -163,9 +164,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 +190,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 +203,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..9b8dd64 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -87,11 +87,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(),
diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java
index 5f03252..ebbe5fc 100644
--- a/src/com/android/launcher3/notification/SwipeHelper.java
+++ b/src/com/android/launcher3/notification/SwipeHelper.java
@@ -24,23 +24,20 @@
 import android.content.Context;
 import android.graphics.RectF;
 import android.os.Handler;
+import android.util.ArrayMap;
 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 String TAG = "SwipeHelper";
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
     private static final boolean CONSTRAIN_SWIPE = true;
@@ -50,10 +47,10 @@
     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 float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
+    private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
+    private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
+    private static final 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
@@ -61,12 +58,12 @@
     private float mMinSwipeProgress = 0f;
     private float mMaxSwipeProgress = 1f;
 
-    private FlingAnimationUtils mFlingAnimationUtils;
+    private final FlingAnimationUtils mFlingAnimationUtils;
     private float mPagingTouchSlop;
-    private Callback mCallback;
-    private Handler mHandler;
-    private int mSwipeDirection;
-    private VelocityTracker mVelocityTracker;
+    private final Callback mCallback;
+    private final Handler mHandler;
+    private final int mSwipeDirection;
+    private final VelocityTracker mVelocityTracker;
 
     private float mInitialTouchPos;
     private float mPerpendicularInitialTouchPos;
@@ -80,14 +77,14 @@
     private boolean mLongPressSent;
     private LongPressListener mLongPressListener;
     private Runnable mWatchLongPress;
-    private long mLongPressTimeout;
+    private final long mLongPressTimeout;
 
     final private int[] mTmpPos = new int[2];
-    private int mFalsingThreshold;
+    private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
     private boolean mDisableHwLayers;
 
-    private HashMap<View, Animator> mDismissPendingMap = new HashMap<>();
+    private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
 
     public SwipeHelper(int swipeDirection, Callback callback, Context context) {
         mCallback = callback;
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..8bcb979 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;
 
@@ -52,7 +50,6 @@
         Launcher l = Launcher.getLauncher(context);
         setOnTouchListener(l.getHapticFeedbackTouchListener());
         setOnClickListener(l);
-        setOnLongClickListener(l);
         setOnFocusChangeListener(l.mFocusHandler);
     }
 
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 3ceba84..6b992fc 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -137,7 +137,6 @@
         mAllAppsHandle.setImageDrawable(getCaretDrawable());
         mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
         mAllAppsHandle.setOnClickListener(mLauncher);
-        mAllAppsHandle.setOnLongClickListener(mLauncher);
         mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
         mLauncher.setAllAppsButton(mAllAppsHandle);
     }
@@ -221,7 +220,7 @@
      */
     public void updateColor(ExtractedColors extractedColors) {
         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..77375fa 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -20,19 +20,21 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.CornerPathEffect;
+import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.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,7 +42,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
+import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -52,13 +54,13 @@
 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 +73,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 +94,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;
@@ -106,6 +122,8 @@
     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,79 @@
                 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;
+                boolean notificationFooterHasIcons = numNotifications > 1;
                 int footerHeight = notificationFooterHasIcons ?
                         res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
                 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;
+                }
+                if (shouldUnroundBottomCorners) {
+                    roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+                }
+                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) {
+                    // Condense shortcuts height when there are notifications.
+                    item.getLayoutParams().height = res.getDimensionPixelSize(
+                            R.dimen.bg_popup_item_condensed_height);
                 }
                 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, mNotificationItemView == null
+                ? R.attr.popupColorPrimary : R.attr.popupColorSecondary);
+        mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
+        if (numNotifications > 0) {
+            mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
+        }
     }
 
     protected PopupItemView getItemViewAt(int index) {
@@ -296,45 +345,31 @@
         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 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((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+        revealAnim.setInterpolator(new AccelerateDecelerateInterpolator());
+
+        // 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 +380,26 @@
             }
         });
 
-        // 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();
+    /**
+     * 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 +459,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) {
@@ -505,7 +553,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 +577,7 @@
      */
     public DragOptions.PreDragCondition createPreDragCondition() {
         return new DragOptions.PreDragCondition() {
+
             @Override
             public boolean shouldStartDrag(double distanceDragged) {
                 return distanceDragged > mStartDragThreshold;
@@ -536,15 +585,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 +651,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 +676,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 +706,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 +759,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 +834,43 @@
         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();
+
+        // Animate the arrow.
+        Animator arrowScale = createArrowScaleAnim(0).setDuration(res.getInteger(
+                R.integer.config_popupArrowOpenDuration));
+
+        // 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((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+        revealAnim.setInterpolator(new AccelerateDecelerateInterpolator());
 
-        shortcutAnims.addListener(new AnimatorListenerAdapter() {
+        // Animate original icon's text back in.
+        closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
+
+        closeAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mOpenCloseAnimator = null;
@@ -813,8 +881,9 @@
                 }
             }
         });
-        mOpenCloseAnimator = shortcutAnims;
-        shortcutAnims.start();
+        mOpenCloseAnimator = closeAnim;
+        closeAnim.playSequentially(arrowScale, revealAnim);
+        closeAnim.start();
         mOriginalIcon.forceHideBadge(false);
     }
 
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..00e2644 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();
         }
     }
 
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/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index df7f695..5ce78dc 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).isPromise();
         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..8785a56 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -18,10 +18,13 @@
 
 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.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -32,7 +35,7 @@
 import com.android.launcher3.Launcher;
 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,19 @@
             // 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);
+
+                View divider = LayoutInflater.from(getContext()).inflate(
+                        R.layout.horizontal_divider, this, false);
+
+                boolean iconsAreBelowShortcuts = mShortcutsLayout.getChildCount() > 0;
+                if (iconsAreBelowShortcuts) {
+                    mContent.addView(divider);
+                    mContent.addView(mSystemShortcutIcons);
+                } else {
+                    mContent.addView(divider, 0);
+                    mContent.addView(mSystemShortcutIcons, 0);
+                }
             }
             mSystemShortcutIcons.addView(shortcutView, index);
         } else {
@@ -166,6 +186,114 @@
     }
 
     /**
+     * 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));
+        }
+        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 +338,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..8d43518 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,8 +10,6 @@
 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 java.io.FileDescriptor;
@@ -200,11 +197,6 @@
         }
 
         @Override
-        public AllAppsSearchBarController getAllAppsSearchBarController() {
-            return null;
-        }
-
-        @Override
         public List<ComponentKey> getPredictedApps() {
             // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
             return new ArrayList<>();
@@ -216,11 +208,6 @@
         }
 
         @Override
-        public void setLauncherSearchCallback(Object callbacks) {
-            // Do nothing
-        }
-
-        @Override
         public void onAttachedToWindow() {
         }
 
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/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/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..091dd84 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.isAtLeastO() && !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/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/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
new file mode 100644
index 0000000..6b5b095
--- /dev/null
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 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[3];
+
+    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.isAtLeastO()) {
+                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..4cb6ca8 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,12 +16,10 @@
 
 package com.android.launcher3.util;
 
-import android.util.Log;
 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 java.util.ArrayList;
@@ -34,7 +32,7 @@
         OnAttachStateChangeListener {
 
     private final ArrayList<Runnable> mTasks = new ArrayList<>();
-    private final DeferredHandler mHandler;
+    private final Executor mExecutor;
 
     private Launcher mLauncher;
     private View mAttachedView;
@@ -43,8 +41,8 @@
     private boolean mLoadAnimationCompleted;
     private boolean mFirstDrawCompleted;
 
-    public ViewOnDrawExecutor(DeferredHandler handler) {
-        mHandler = handler;
+    public ViewOnDrawExecutor(Executor executor) {
+        mExecutor = executor;
     }
 
     public void attachTo(Launcher launcher) {
@@ -92,7 +90,7 @@
         // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
         if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
             for (final Runnable r : mTasks) {
-                mHandler.post(r);
+                mExecutor.execute(r);
             }
             markCompleted();
         }
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
new file mode 100644
index 0000000..c0b5fe1
--- /dev/null
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 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, don't draw any shadow
+        int alpha = Color.alpha(getCurrentTextColor());
+        if (alpha == 0) {
+            getPaint().clearShadowLayer();
+            super.onDraw(canvas);
+            return;
+        }
+
+        // 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();
+        }
+    }
+}
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/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/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 5fe00c2..fff3472 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;
 
@@ -48,6 +47,7 @@
 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.TouchController;
 
 import java.util.List;
@@ -69,7 +69,6 @@
     private Interpolator mFastOutSlowInInterpolator;
     private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
     private Rect mInsets;
-    private boolean mWasNavBarLight;
     private VerticalPullDetector mVerticalPullDetector;
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs) {
@@ -77,11 +76,12 @@
     }
 
     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();
+        mFastOutSlowInInterpolator =
+                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
         mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
         mInsets = new Rect();
         mVerticalPullDetector = new VerticalPullDetector(context);
@@ -103,8 +103,6 @@
 
         onWidgetsBound();
 
-        mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
-                & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
         mLauncher.getDragLayer().addView(this);
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         setTranslationY(mTranslationYClosed);
@@ -180,7 +178,8 @@
             return;
         }
         mIsOpen = true;
-        setLightNavBar(true);
+        mLauncher.getSystemUiController().updateUiState(
+                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, SystemUiController.FLAG_LIGHT_NAV);
         if (animate) {
             mOpenCloseAnimator.setValues(new PropertyListBuilder()
                     .translationY(mTranslationYOpen).build());
@@ -211,7 +210,8 @@
                     mIsOpen = false;
                     mVerticalPullDetector.finishedScrolling();
                     ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this);
-                    setLightNavBar(mWasNavBarLight);
+                    mLauncher.getSystemUiController().updateUiState(
+                            SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
                 }
             });
             mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState()
@@ -219,15 +219,12 @@
             mOpenCloseAnimator.start();
         } else {
             setTranslationY(mTranslationYClosed);
-            setLightNavBar(mWasNavBarLight);
+            mLauncher.getSystemUiController().updateUiState(
+                    SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
             mIsOpen = false;
         }
     }
 
-    private void setLightNavBar(boolean lightNavBar) {
-        mLauncher.activateLightSystemBars(lightNavBar, false /* statusBar */, true /* navBar */);
-    }
-
     @Override
     protected boolean isOfType(@FloatingViewType int type) {
         return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 4e296bf..14a9d17 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -139,8 +139,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
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%
rename from src_config/com/android/launcher3/config/ProviderConfig.java
rename 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_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 73%
rename from src_config/com/android/launcher3/config/FeatureFlags.java
rename to src_flags/com/android/launcher3/config/FeatureFlags.java
index 4e337a2..42a110c 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -20,6 +20,9 @@
  * Defines a set of flags used to control various launcher behaviors
  */
 public final class FeatureFlags {
+
+    public static final boolean IS_DOGFOOD_BUILD = true;
+
     private FeatureFlags() {}
 
     // Custom flags go below this
@@ -28,12 +31,19 @@
     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;
+    public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
     // 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;
-
+    public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = true;
+    // When enabled the promise icon is visible in all apps while installation an app.
+    public static boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
+    // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
+    public static boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
+    // When enabled allows use of physics based motions in the Launcher.
+    public static boolean LAUNCHER3_PHYSICS = true;
+    // When enabled allows use of spring motions on the icons.
+    public static 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;
@@ -45,10 +55,12 @@
     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.
+    // 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;
 }
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/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
rename to tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index e9c2ebc..58dc0c4 100644
--- a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
 
 import android.content.ComponentName;
 import android.test.InstrumentationTestCase;
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index d0ba907..4c80902 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;
@@ -21,6 +21,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.verify;
@@ -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) { }
@@ -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/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index df2b662..97f7b50 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -39,13 +39,12 @@
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.ui.LauncherInstrumentationTestCase;
 import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 
 import java.util.Set;
 import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -340,7 +339,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/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