Merge "Defining HomeScreenElementTheme for elements which appear on top of wallpaper" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 713d082..6cb40c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -31,6 +31,7 @@
 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 86ae73e..50fb2d7 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -49,7 +49,7 @@
         android:fullBackupOnly="true"
         android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
-        android:icon="@mipmap/ic_launcher_home"
+        android:icon="@drawable/ic_launcher_home"
         android:label="@string/derived_app_name"
         android:largeHeap="@bool/config_largeHeap"
         android:restoreAnyVersion="true"
@@ -58,12 +58,21 @@
         <!-- Intent received used to install shortcuts from other applications -->
         <receiver
             android:name="com.android.launcher3.InstallShortcutReceiver"
-            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
+            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT"
+            android:enabled="@bool/enable_install_shortcut_api" >
             <intent-filter>
                 <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
             </intent-filter>
         </receiver>
 
+        <!-- Intent received when a session is committed -->
+        <receiver
+            android:name="com.android.launcher3.SessionCommitReceiver" >
+            <intent-filter>
+                <action android:name="android.content.pm.action.SESSION_COMMITTED" />
+            </intent-filter>
+        </receiver>
+
         <!-- Intent received used to initialize a restored widget -->
         <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
             <intent-filter>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 418b3a3..bcb522b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -54,7 +54,7 @@
         android:fullBackupOnly="true"
         android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
-        android:icon="@mipmap/ic_launcher_home"
+        android:icon="@drawable/ic_launcher_home"
         android:label="@string/derived_app_name"
         android:theme="@style/LauncherTheme"
         android:largeHeap="@bool/config_largeHeap"
@@ -70,9 +70,10 @@
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:windowSoftInputMode="adjustPan"
+            android:windowSoftInputMode="adjustPan|stateUnchanged"
             android:screenOrientation="nosensor"
             android:configChanges="keyboard|keyboardHidden|navigation"
+            android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
             android:enabled="true">
@@ -134,7 +135,7 @@
             android:theme="@android:style/Theme.NoDisplay"
             android:label="* HPROF"
             android:excludeFromRecents="true"
-            android:icon="@mipmap/ic_launcher_home"
+            android:icon="@drawable/ic_launcher_home"
             >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -146,7 +147,7 @@
         <activity
             android:name="com.android.launcher3.testing.ToggleWeightWatcher"
             android:label="Show Mem"
-            android:icon="@mipmap/ic_launcher_home">
+            android:icon="@drawable/ic_launcher_home">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/build.gradle b/build.gradle
index 51ac5a1..d6dbbab 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,7 +38,7 @@
     sourceSets {
         main {
             res.srcDirs = ['res']
-            java.srcDirs = ['src', 'src_config']
+            java.srcDirs = ['src', 'src_flags']
             manifest.srcFile 'AndroidManifest-common.xml'
             proto.srcDirs 'protos/'
         }
@@ -90,6 +90,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/protos/launcher_dump.proto b/protos/launcher_dump.proto
new file mode 100644
index 0000000..dc8fbda
--- /dev/null
+++ b/protos/launcher_dump.proto
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.model";
+option java_outer_classname = "LauncherDumpProto";
+
+package model;
+
+message DumpTarget {
+  enum Type {
+    NONE = 0;
+    ITEM = 1;
+    CONTAINER = 2;
+  }
+
+  optional Type type = 1;
+  optional int32 page_id = 2;
+  optional int32 grid_x = 3;
+  optional int32 grid_y = 4;
+
+  // For container types only
+  optional ContainerType container_type = 5;
+
+  // For item types only
+  optional ItemType item_type = 6;
+
+  optional string package_name = 7; // All ItemTypes except UNKNOWN type
+  optional string component = 8;   // All ItemTypes except UNKNOWN type
+  optional string item_id = 9; // For Pinned Shortcuts and appWidgetId
+
+  optional int32 span_x = 10 [default = 1];// Used for ItemType.WIDGET
+  optional int32 span_y = 11 [default = 1];// Used for ItemType.WIDGET
+  optional UserType user_type = 12;
+}
+
+// Used to define what type of item a Target would represent.
+enum ItemType {
+  UNKNOWN_ITEMTYPE = 0;  // Launcher specific items
+  APP_ICON = 1; // Regular app icons
+  WIDGET = 2;   // Elements from AppWidgetManager
+  SHORTCUT = 3; // ShortcutManager
+}
+
+// Used to define what type of container a Target would represent.
+enum ContainerType {
+  UNKNOWN_CONTAINERTYPE = 0;
+  WORKSPACE = 1;
+  HOTSEAT = 2;
+  FOLDER = 3;
+}
+
+// Used to define what type of control a Target would represent.
+enum UserType {
+  DEFAULT = 0;
+  WORK = 1;
+}
+
+// Main message;
+message LauncherImpression {
+  repeated DumpTarget targets = 1;
+}
diff --git a/res/animator/all_apps_fastscroll_icon_anim.xml b/res/animator/all_apps_fastscroll_icon_anim.xml
new file mode 100644
index 0000000..380b009
--- /dev/null
+++ b/res/animator/all_apps_fastscroll_icon_anim.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_activated="true">
+        <set>
+            <objectAnimator
+                android:duration="225"
+                android:propertyName="scaleX"
+                android:valueTo="1.15"
+                android:valueType="floatType" />
+            <objectAnimator
+                android:duration="225"
+                android:propertyName="scaleY"
+                android:valueTo="1.15"
+                android:valueType="floatType" />
+        </set>
+    </item>
+
+    <item>
+        <set>
+            <objectAnimator
+                android:duration="275"
+                android:propertyName="scaleX"
+                android:valueTo="1"
+                android:valueType="floatType" />
+            <objectAnimator
+                android:duration="275"
+                android:propertyName="scaleY"
+                android:valueTo="1"
+                android:valueType="floatType" />
+        </set>
+    </item>
+
+</selector>
\ No newline at end of file
diff --git a/res/animator-v21/overview_button_anim.xml b/res/animator/overview_button_anim.xml
similarity index 100%
rename from res/animator-v21/overview_button_anim.xml
rename to res/animator/overview_button_anim.xml
diff --git a/res/drawable-v26/mask_drawable_wrapper.xml b/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
similarity index 90%
rename from res/drawable-v26/mask_drawable_wrapper.xml
rename to res/drawable-v26/adaptive_icon_drawable_wrapper.xml
index 36efd0f..7ad8e2c 100644
--- a/res/drawable-v26/mask_drawable_wrapper.xml
+++ b/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
@@ -14,9 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<maskable-icon xmlns:android="http://schemas.android.com/apk/res/android">
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:color="#FFE0E0E0"/>
     <foreground>
         <com.android.launcher3.graphics.FixedScaleDrawable />
     </foreground>
-</maskable-icon>
+</adaptive-icon>
diff --git a/res/drawable-v26/mask_drawable_wrapper.xml b/res/drawable-v26/ic_launcher_home.xml
similarity index 65%
copy from res/drawable-v26/mask_drawable_wrapper.xml
copy to res/drawable-v26/ic_launcher_home.xml
index 36efd0f..7038775 100644
--- a/res/drawable-v26/mask_drawable_wrapper.xml
+++ b/res/drawable-v26/ic_launcher_home.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 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,9 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<maskable-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:color="#FFE0E0E0"/>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/icon_background" />
     <foreground>
-        <com.android.launcher3.graphics.FixedScaleDrawable />
+        <bitmap android:src="@mipmap/ic_launcher_home_foreground"/>
     </foreground>
-</maskable-icon>
+</adaptive-icon>
diff --git a/res/drawable/bg_pill_focused.xml b/res/drawable/bg_pill_focused.xml
deleted file mode 100644
index 54075d9..0000000
--- a/res/drawable/bg_pill_focused.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true">
-        <shape android:shape="rectangle">
-            <stroke android:color="?android:attr/colorControlActivated" android:width="2dp" />
-            <corners android:radius="@dimen/bg_pill_radius" />
-        </shape>
-    </item>
-</selector>
\ No newline at end of file
diff --git a/res/drawable/bg_white_pill.xml b/res/drawable/bg_white_pill.xml
deleted file mode 100644
index f92f739..0000000
--- a/res/drawable/bg_white_pill.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="#FFFFFF" />
-    <corners android:radius="@dimen/bg_pill_radius" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/bg_white_pill_top.xml b/res/drawable/bg_white_pill_top.xml
deleted file mode 100644
index 9988b29..0000000
--- a/res/drawable/bg_white_pill_top.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="#FFFFFF" />
-    <corners android:topLeftRadius="@dimen/bg_pill_radius"
-             android:topRightRadius="@dimen/bg_pill_radius" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/bg_white_pill_bottom.xml b/res/drawable/bg_white_round_rect.xml
similarity index 86%
rename from res/drawable/bg_white_pill_bottom.xml
rename to res/drawable/bg_white_round_rect.xml
index a1ea48c..c7f786f 100644
--- a/res/drawable/bg_white_pill_bottom.xml
+++ b/res/drawable/bg_white_round_rect.xml
@@ -17,6 +17,5 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <solid android:color="#FFFFFF" />
-    <corners android:bottomLeftRadius="@dimen/bg_pill_radius"
-             android:bottomRightRadius="@dimen/bg_pill_radius" />
+    <corners android:radius="@dimen/bg_round_rect_radius" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/horizontal_ellipsis.xml b/res/drawable/horizontal_ellipsis.xml
new file mode 100644
index 0000000..ad08529
--- /dev/null
+++ b/res/drawable/horizontal_ellipsis.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="@dimen/horizontal_ellipsis_size"
+        android:height="@dimen/horizontal_ellipsis_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorSecondary" >
+
+    <path
+        android:pathData="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
+        android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_instant_app.xml b/res/drawable/ic_instant_app.xml
new file mode 100644
index 0000000..be5a3e0
--- /dev/null
+++ b/res/drawable/ic_instant_app.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/badge_size"
+    android:height="@dimen/badge_size"
+    android:viewportWidth="18"
+    android:viewportHeight="18">
+
+    <path
+        android:fillColor="@android:color/black"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/black"
+        android:fillAlpha="0.87"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
+</vector>
diff --git a/res/drawable/bg_white_pill_bottom.xml b/res/drawable/ic_launcher_home.xml
similarity index 65%
copy from res/drawable/bg_white_pill_bottom.xml
copy to res/drawable/ic_launcher_home.xml
index a1ea48c..a6f2519 100644
--- a/res/drawable/bg_white_pill_bottom.xml
+++ b/res/drawable/ic_launcher_home.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,10 +13,6 @@
      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:bottomLeftRadius="@dimen/bg_pill_radius"
-             android:bottomRightRadius="@dimen/bg_pill_radius" />
-</shape>
\ No newline at end of file
+<bitmap
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/ic_launcher_home" />
diff --git a/res/drawable/ic_star_rating.xml b/res/drawable/ic_star_rating.xml
new file mode 100644
index 0000000..4e34fa3
--- /dev/null
+++ b/res/drawable/ic_star_rating.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="12dp"
+    android:height="12dp"
+    android:viewportWidth="12"
+    android:viewportHeight="12">
+
+    <path
+        android:fillColor="#FB8C00"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9.76511755 11.9348136 L 8.33665684 7.16088817 L 12.080006 4.41656311 L 7.49967039 4.41856896 L 6.03138903 0 L 4.57932894 4.41856896 L -1.34115008e-16 4.41656311 L 3.72612122 7.16088817 L 2.29967385 11.9348136 L 6.03138903 8.82574452 Z" />
+</vector>
\ No newline at end of file
diff --git a/res/interpolator/folder_closing_interpolator.xml b/res/interpolator/folder_closing_interpolator.xml
new file mode 100644
index 0000000..e6e012e
--- /dev/null
+++ b/res/interpolator/folder_closing_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.8"
+    android:controlY1="0"
+    android:controlX2="0.8"
+    android:controlY2="1"/>
diff --git a/res/interpolator/folder_opening_interpolator.xml b/res/interpolator/folder_opening_interpolator.xml
new file mode 100644
index 0000000..b95d454
--- /dev/null
+++ b/res/interpolator/folder_opening_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/folder_preview_item_closing_interpolator.xml b/res/interpolator/folder_preview_item_closing_interpolator.xml
new file mode 100644
index 0000000..1d77081
--- /dev/null
+++ b/res/interpolator/folder_preview_item_closing_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="1"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="0"/>
diff --git a/res/interpolator/folder_preview_item_opening_interpolator.xml b/res/interpolator/folder_preview_item_opening_interpolator.xml
new file mode 100644
index 0000000..dcf0157
--- /dev/null
+++ b/res/interpolator/folder_preview_item_opening_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/all_apps_discovery_item.xml b/res/layout/all_apps_discovery_item.xml
new file mode 100644
index 0000000..2b21ef5
--- /dev/null
+++ b/res/layout/all_apps_discovery_item.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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.discovery.AppDiscoveryItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clickable="true"
+    android:background="?android:selectableItemBackground">
+
+    <ImageView
+        android:id="@+id/image"
+        android:layout_width="56dp"
+        android:layout_height="56dp"
+        android:padding="8dp"
+        android:scaleType="fitCenter"/>
+
+    <ImageView
+        android:id="@+id/badge"
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_instant_app"
+        android:layout_marginRight="6dp"
+        android:layout_marginBottom="6dp"
+        android:layout_alignRight="@+id/image"
+        android:layout_alignBottom="@+id/image"
+        android:clickable="false"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_centerVertical="true"
+        android:layout_toRightOf="@id/image">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="?android:textColorSecondary"
+            android:textSize="15sp"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <TextView
+                android:id="@+id/rating"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="?android:textColorSecondary"
+                android:textSize="14sp"
+                android:layout_gravity="center_vertical"/>
+
+            <com.android.launcher3.discovery.RatingView
+                android:id="@+id/rating_view"
+                android:layout_width="80dp"
+                android:layout_height="16dp"
+                android:layout_marginLeft="5dp"
+                android:layout_marginRight="5dp"
+                android:layout_gravity="center_vertical"/>
+
+            <TextView
+                android:id="@+id/review_count"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="5dp"
+                android:textColor="?android:textColorHint"
+                android:textSize="14sp"
+                android:layout_gravity="center_vertical"/>
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1"/>
+
+            <TextView
+                android:id="@+id/price"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="?android:textColorHint"
+                android:textSize="14sp"
+                android:layout_marginRight="12dp"
+                android:textAllCaps="true"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <ImageView
+        android:importantForAccessibility="no"
+        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:src="@drawable/all_apps_divider"
+        android:scaleType="fitXY"
+        android:focusable="false" />
+</com.android.launcher3.discovery.AppDiscoveryItemView>
\ No newline at end of file
diff --git a/res/layout/all_apps_discovery_loading_divider.xml b/res/layout/all_apps_discovery_loading_divider.xml
new file mode 100644
index 0000000..c7b5ad2
--- /dev/null
+++ b/res/layout/all_apps_discovery_loading_divider.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="20dp"
+    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingRight="@dimen/container_fastscroll_thumb_max_width">
+
+    <ProgressBar
+        android:id="@+id/loadingProgressBar"
+        style="@android:style/Widget.Material.ProgressBar.Horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="6dp"
+        android:maxHeight="6dp"
+        android:indeterminate="true"
+        android:layout_gravity="center"/>
+
+    <View
+        android:id="@+id/loadedDivider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@drawable/all_apps_divider"
+        android:layout_gravity="center"
+        android:visibility="invisible"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 1e83041..ca0cbcc 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -20,6 +20,7 @@
     android:id="@+id/icon"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
     launcher:iconDisplay="all_apps"
     launcher:centerVertically="true"
     android:paddingLeft="4dp"
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 6c1d4da..b2ed709 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -17,18 +17,16 @@
 <com.android.launcher3.shortcuts.DeepShortcutView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:layout_width="@dimen/bg_pill_width"
-    android:layout_height="@dimen/bg_pill_height"
-    android:elevation="@dimen/deep_shortcuts_elevation"
-    android:background="@drawable/bg_white_pill" >
+    android:layout_width="@dimen/bg_popup_item_width"
+    android:layout_height="@dimen/bg_popup_item_height" >
 
     <com.android.launcher3.shortcuts.DeepShortcutTextView
         style="@style/BaseIcon"
         android:id="@+id/deep_shortcut"
-        android:background="@drawable/bg_pill_focused"
+        android:background="?android:attr/selectableItemBackground"
         android:gravity="start|center_vertical"
         android:textAlignment="viewStart"
-        android:paddingStart="@dimen/bg_pill_height"
+        android:paddingStart="@dimen/bg_popup_item_height"
         android:paddingEnd="@dimen/deep_shortcut_padding_end"
         android:drawableEnd="@drawable/deep_shortcuts_drag_handle"
         android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
@@ -40,10 +38,18 @@
         android:elevation="@dimen/deep_shortcuts_elevation" />
 
     <View
-        android:id="@+id/popup_item_icon"
+        android:id="@+id/icon"
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="@dimen/deep_shortcut_icon_size"
         android:layout_margin="@dimen/deep_shortcut_padding_start"
         android:layout_gravity="start" />
 
+    <View
+        android:id="@+id/divider"
+        android:layout_width="@dimen/deep_shortcuts_divider_width"
+        android:layout_height="@dimen/popup_item_divider_height"
+        android:layout_gravity="end|bottom"
+        android:visibility="gone"
+        android:background="?android:attr/listDivider" />
+
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/notification.xml b/res/layout/notification.xml
index d828c4a..6922ad9 100644
--- a/res/layout/notification.xml
+++ b/res/layout/notification.xml
@@ -17,10 +17,11 @@
 <com.android.launcher3.notification.NotificationItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/notification_view"
-    android:layout_width="@dimen/bg_pill_width"
+    android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="wrap_content"
     android:elevation="@dimen/deep_shortcuts_elevation"
-    android:background="@drawable/bg_white_pill">
+    android:background="@drawable/bg_white_round_rect"
+    android:backgroundTint="@color/notification_color_beneath">
 
     <RelativeLayout
         android:layout_width="match_parent"
@@ -28,33 +29,49 @@
         android:orientation="vertical"
         android:clipChildren="false">
 
-        <TextView
+        <com.android.launcher3.notification.NotificationHeaderView
             android:id="@+id/header"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_footer_collapsed_height"
-            android:gravity="center_vertical"
-            android:textAlignment="center"
-            android:text="@string/notifications_header"
-            android:elevation="@dimen/notification_elevation"
-            android:background="@drawable/bg_white_pill_top" />
-
-        <View
-            android:id="@+id/divider"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_divider_height"
-            android:layout_below="@id/header" />
+            android:layout_height="@dimen/notification_header_height"
+            android:orientation="horizontal"
+            android:paddingStart="@dimen/notification_padding"
+            android:background="@color/notification_header_background_color"
+            android:elevation="@dimen/notification_elevation">
+            <TextView
+                android:id="@+id/notification_count"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="@dimen/notification_header_padding_after_count"
+                android:textSize="@dimen/notification_main_text_size"
+                android:textColor="?android:attr/textColorPrimary" />
+            <TextView
+                android:id="@+id/notification_text"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="start|center_vertical"
+                android:textSize="@dimen/notification_main_text_size"
+                android:textColor="?android:attr/textColorSecondary" />
+        </com.android.launcher3.notification.NotificationHeaderView>
 
         <include layout="@layout/notification_main"
             android:id="@+id/main_view"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/bg_pill_height"
-            android:layout_below="@id/divider" />
+            android:layout_height="@dimen/notification_main_height"
+            android:layout_below="@id/header" />
+
+        <View
+            android:id="@+id/divider"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/popup_item_divider_height"
+            android:background="@color/divider_color"
+            android:layout_below="@id/main_view"/>
 
         <include layout="@layout/notification_footer"
             android:id="@+id/footer"
             android:layout_width="match_parent"
             android:layout_height="@dimen/notification_footer_height"
-            android:layout_below="@id/main_view" />
+            android:layout_below="@id/divider" />
 
     </RelativeLayout>
 
diff --git a/res/layout/notification_footer.xml b/res/layout/notification_footer.xml
index ceea24a..f1f5724 100644
--- a/res/layout/notification_footer.xml
+++ b/res/layout/notification_footer.xml
@@ -18,25 +18,29 @@
 <com.android.launcher3.notification.NotificationFooterLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="@drawable/bg_white_pill_bottom"
     android:elevation="@dimen/notification_elevation"
-    android:clipChildren="false" >
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_divider_height"/>
+    android:clipChildren="false"
+    android:layout_gravity="center_vertical"
+    android:background="@color/notification_background_color">
 
     <LinearLayout
         android:id="@+id/icon_row"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal"
+        android:gravity="end|center_vertical"
         android:padding="@dimen/notification_footer_icon_row_padding"
         android:clipToPadding="false"
         android:clipChildren="false"/>
 
+    <View
+        android:id="@+id/overflow"
+        android:layout_width="@dimen/horizontal_ellipsis_size"
+        android:layout_height="@dimen/horizontal_ellipsis_size"
+        android:background="@drawable/horizontal_ellipsis"
+        android:layout_marginStart="@dimen/horizontal_ellipsis_offset"
+        android:layout_gravity="start|center_vertical" />
+
 </com.android.launcher3.notification.NotificationFooterLayout>
 
diff --git a/res/layout/notification_main.xml b/res/layout/notification_main.xml
index 84827f1..9c5847f 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -19,42 +19,46 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="horizontal"
     android:focusable="true"
     android:elevation="@dimen/notification_elevation" >
 
+    <LinearLayout
+        android:id="@+id/text_and_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:gravity="center_vertical"
+        android:background="@color/notification_background_color"
+        android:paddingStart="@dimen/notification_padding"
+        android:paddingEnd="@dimen/notification_main_text_padding_end">
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAlignment="viewStart"
+            android:fontFamily="sans-serif"
+            android:textSize="@dimen/notification_main_text_size"
+            android:textColor="?android:attr/textColorPrimary"
+            android:lines="1"
+            android:ellipsize="end" />
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif"
+            android:textSize="@dimen/notification_main_text_size"
+            android:textColor="?android:attr/textColorSecondary"
+            android:lines="1"
+            android:ellipsize="end" />
+    </LinearLayout>
+
     <View
         android:id="@+id/popup_item_icon"
         android:layout_width="@dimen/notification_icon_size"
         android:layout_height="@dimen/notification_icon_size"
-        android:layout_marginStart="@dimen/notification_icon_margin_start"
-        android:layout_gravity="center_vertical" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:layout_marginStart="@dimen/notification_text_margin_start"
-        android:gravity="center_vertical">
-        <TextView
-            android:id="@+id/title"
-            android:textAlignment="viewStart"
-            android:fontFamily="sans-serif"
-            android:textSize="14sp"
-            android:textColor="?android:attr/textColorSecondary"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-        <TextView
-            android:id="@+id/text"
-            android:paddingEnd="4dp"
-            android:textSize="12sp"
-            android:textAlignment="viewStart"
-            android:fontFamily="sans-serif"
-            android:textColor="?android:attr/textColorTertiary"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-    </LinearLayout>
+        android:layout_marginEnd="@dimen/notification_padding"
+        android:layout_gravity="center_vertical|end" />
 
 </com.android.launcher3.notification.NotificationMainView>
 
diff --git a/res/layout/shortcuts_item.xml b/res/layout/shortcuts_item.xml
new file mode 100644
index 0000000..8b20bcb
--- /dev/null
+++ b/res/layout/shortcuts_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.shortcuts.ShortcutsItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    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">
+
+    <LinearLayout
+        android:id="@+id/deep_shortcuts"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+    </LinearLayout>
+
+</com.android.launcher3.shortcuts.ShortcutsItemView>
diff --git a/res/mipmap-hdpi/ic_launcher_home_foreground.png b/res/mipmap-hdpi/ic_launcher_home_foreground.png
new file mode 100644
index 0000000..d068d92
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_home_foreground.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_home_foreground.png b/res/mipmap-mdpi/ic_launcher_home_foreground.png
new file mode 100644
index 0000000..0ed9f4d
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_home_foreground.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_home_foreground.png b/res/mipmap-xhdpi/ic_launcher_home_foreground.png
new file mode 100644
index 0000000..7a9daf5
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher_home_foreground.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_home_foreground.png b/res/mipmap-xxhdpi/ic_launcher_home_foreground.png
new file mode 100644
index 0000000..03b493e
--- /dev/null
+++ b/res/mipmap-xxhdpi/ic_launcher_home_foreground.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index dd427e3..0fa856c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en hou om \'n legstuk op te tel of gebruik gepasmaakte handelinge."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Raak en hou om op tuisskerm te plaas"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Plaas outomaties"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Deursoek programme"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Laai tans programme …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Geen programme gevind wat met \"<xliff:g id="QUERY">%1$s</xliff:g>\" ooreenstem nie"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 02d5cc1..199da77 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"አንድ ንዑስ ፕሮግራም ለመምረጥ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"በመነሻ ማያ ገጽ ላይ ለማስቀመጥ ነካ ያድርጉ እና ይያዙ"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"በራስ-ሰር ያስቀምጡ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"መተግበሪያዎችን ይፈልጉ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"መተግበሪያዎችን በመጫን ላይ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ፈልግ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 437e974..0c74f93 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"انقر نقرًا مزدوجًا مع الاستمرار لاختيار أداة أو استخدم الإجراءات المخصصة."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏العرض %1$d الطول %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"المس الرمز مع الاستمرار لوضعه على الشاشة الرئيسية"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"إضافة الرمز إلى الشاشة الرئيسية تلقائيًا"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"البحث في التطبيقات"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"جارٍ تحميل التطبيقات…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"لم يتم العثور على أية تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
     <string name="abandoned_search" msgid="891119232568284442">"بحث"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index fe0ef4f..f0e7233 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Vidceti götürmək üçün &amp; iki dəfə toxunub saxlayın və ya fərdi fəaliyyətləri istifadə edin."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Əsas ekranda yerləşdirmək üçün toxunub saxlayın"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Avtomatik yerləşdirin"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index bd7b874..388b1b4 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite i zadržite da biste izabrali vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dodirnite i zadržite da biste postavili na početni ekran"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Postavi automatski"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretražite aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index b15dc60..21edf62 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Дакраніцеся і ўтрымлівайце, каб размясціць на галоўным экране"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Размясціць аўтаматычна"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук у Праграмах"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ідзе загрузка праграм…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 13d1641..865bafc 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Докоснете двукратно и задръжте за избор на приспособление или използвайте персонализирани действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d и височина %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Докоснете и задръжте, за да поставите на началния екран"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Автоматично поставяне"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Търсене в приложенията"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Приложенията се зареждат…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Търсене"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 6137f03..5f94992 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"কোনো উইজেট বেছে নিতে দুবার-আলতো চেপে ধরে থাকুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন৷"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"হোম স্ক্রিনে রাখতে টাচ করে ধরে থাকুন"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"স্বয়ংক্রিয়ভাবে রাখুন"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"অ্যাপ্লিকেশানগুলি লোড হচ্ছে..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
     <string name="abandoned_search" msgid="891119232568284442">"অনুসন্ধান"</string>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 705ca04..fb5a24a 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite &amp; i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dodirnite i držite da postavite na početni ekran"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Postavi automatski"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija koja odgovara upitu \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index c12ec8d..a3f8f07 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Fes doble toc i mantén premut per seleccionar un widget o per utilitzar les accions personalitzades."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Toca i mantén premut l\'element per col·locar-lo a la pantalla d\'inici"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Col·loca automàticament"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca a les aplicacions"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"S\'estan carregant les aplicacions..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No s\'ha trobat cap aplicació que coincideixi amb <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 8eacd0a..2585983 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvojitým klepnutím a podržením vyberte widget, případně použijte vlastní akce."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Chcete-li položku umístit na plochu, klepněte na ni a podržte ji"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Umístit automaticky"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hledat aplikace"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítání aplikací…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Hledat"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 220bd4b..4b9fb9d 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryk to gange, og hold fingeren nede for at vælge en widget eller bruge tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Tryk, og hold fingeren nede for at placere på startskærmen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Placer automatisk"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søg i Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Indlæser apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 990c901..2f18a00 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Zum Hinzufügen auf Widget doppeltippen und gedrückt halten oder benutzerdefinierte Aktionen verwenden."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Zum Hinzufügen zum Startbildschirm gedrückt halten"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Automatisch hinzufügen"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"In Apps suchen"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps werden geladen..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index c82b7ee..6a4e79e 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Πατήστε δύο φορές παρατεταμένα για επιλογή γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Πλάτος %1$d επί ύψος %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Αγγίξτε παρατεταμένα για να το τοποθετήσετε στην αρχική οθόνη"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Αυτόματη τοποθέτηση"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Αναζήτηση εφαρμογών"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Φόρτωση εφαρμογών…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Δεν βρέθηκαν εφαρμογές για το ερώτημα \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Αναζήτηση"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index baae4b0..0d58f1d 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Touch &amp; hold to place on home screen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Place automatically"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index baae4b0..0d58f1d 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Touch &amp; hold to place on home screen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Place automatically"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index baae4b0..0d58f1d 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Touch &amp; hold to place on home screen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Place automatically"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 250f784..2e9a594 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Presiona dos veces y mantén presionado para elegir un widget o usa una acción personalizada."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Mantén presionado para ubicarlo en la pantalla principal"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Ubicar de manera automática"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Buscar aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No hay aplicaciones que coincidan con <xliff:g id="QUERY">%1$s</xliff:g>."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 1fdf3ef..f11ecab 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dos veces y mantén pulsado el widget que quieras seleccionar o utiliza acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Mantén pulsado el elemento para añadirlo a la pantalla de inicio"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Añadir automáticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index a0ee1e9..257f281 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Topeltpuudutage ja hoidke vidina valimiseks või kohandatud toimingute kasutamiseks."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Puudutage pikalt avaekraanile paigutamiseks"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Paiguta automaatselt"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
@@ -73,6 +75,10 @@
     <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Praegune kuvaseade ei luba pööramist"</string>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index b2aed2e..7e23a44 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Sakatu birritan eta eduki sakatuta widgeta aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Eduki sakatuta jarri nahi duzun tokia hasierako pantailan"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Jarri automatikoki"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Bilatu aplikazioetan"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 88a4fb5..9876d5e 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگه‌دارید یا از اقدامات سفارشی استفاده کنید."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d عرض در %2$d طول"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"برای قرار دادن در صفحه اصلی لمس کنید و نگه‌دارید"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"افزودن خودکار"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"جستجوی برنامه‌ها"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"در حال بارگیری برنامه‌ها..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"هیچ برنامه‌ای مطابق با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
     <string name="abandoned_search" msgid="891119232568284442">"جستجو"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 0aa01b9..1c4bb88 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Valitse widget tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla kohdetta pitkään."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Sijoita aloitusnäytölle koskettamalla pitkään."</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Sijoita automaattisesti"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sovellushaku"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ladataan sovelluksia…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"”<xliff:g id="QUERY">%1$s</xliff:g>” ei palauttanut sovelluksia."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index e261b74..5f00c11 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Touchez 2x un widget et maintenez doigt dessus pour l’ajouter ou utiliser des actions personnalisées"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Maintenez le doigt sur l\'élément pour le placer sur l\'écran d\'accueil"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Placer automatiquement"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher des applications"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 591dff7..034a655 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Appuyez 2 fois et maintenez la pression pour sélectionner widget ou utilisez actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Appuyez de manière prolongée pour placer l\'élément sur l\'écran d\'accueil"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Placer automatiquement"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 766cddb..f32d963 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dúas veces e mantén premido para seleccionar un widget ou utiliza accións personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Mantén premido o elemento para colocalo na pantalla de inicio"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Colocar automaticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Aplicacións de busca"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index c14816a..36cfd6e 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d પહોળાઈ X %2$d ઊંચાઈ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"હોમ સ્ક્રીન પર મૂકવા માટે ટચ કરો અને પકડો"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"આપમેળે મૂકો"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ ઍપ્લિકેશનો"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
     <string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5534768..f11d67b 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"कोई विजेट चुनने के लिए डबल टैप करके रखें या कस्‍टम कार्रवाइयां चुनें."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौड़ाई गुणा %2$d ऊंचाई"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"होम स्क्रीन पर रखने के लिए स्पर्श करके रखें"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"अपने आप रखें"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ऐप्‍स खोजें"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ऐप्स लोड हो रहे हैं..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलान करने वाला कोई ऐप नहीं मिला"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"निकालें"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index ce370f9..3f3adf1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput i držite kako biste podigli widget ili pokušajte prilagođenim radnjama."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dodirnite i zadržite stavku da biste je postavili na početni zaslon"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Postavi automatski"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Učitavanje aplikacija…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index c2003b6..69dae7d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Modul mozgatásához koppintson rá duplán és tartsa lenyomva, vagy használjon egyéni műveleteket."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Tartsa lenyomva, hogy elhelyezhesse a kezdőképernyőn"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Automatikus elhelyezés"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Alkalmazások keresése"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Alkalmazások betöltése…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Egy alkalmazás sem található a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Keresés"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 7884895..47b3219 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Կրկնակի հպեք և պահեք՝ վիջեթ ավելացնելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Հպեք և պահեք՝ գլխավոր էկրանին ավելացնելու համար"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Ավելացնել ավտոմատ կերպով"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Հավելվածների որոնում"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Հավելվածների բեռնում…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Գտնել"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 469c116..1d64cf8 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketuk dua kalip &amp; tahan untuk mengambil widget atau menggunakan tindakan khusus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Sentuh &amp; tahan untuk ditempatkan di layar utama"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Tempatkan otomatis"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Telusuri Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuat Aplikasi..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tidak ditemukan Aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index f39628d..c525f57 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ýttu tvisvar og haltu fingri á græju til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Haltu inni til að setja á heimaskjáinn"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Staðsetja sjálfkrafa"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Leita í forritum"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Leita"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e4e1242..fd522de 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tocca due volte e tieni premuto per scegliere un widget o per utilizzare azioni personalizzate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Tieni premuto per posizionare l\'elemento nella schermata Home"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Posiziona automaticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca app"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Caricamento di app…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index c04bf12..e037d21 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"הקש פעמיים וגע נגיעה רציפה בווידג\'ט כדי לבחור בו, או השתמש בפעולות מותאמות אישית."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏רוחב %1$d על גובה %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"גע והחזק כדי למקם במסך דף הבית"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"מקם אוטומטית"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"חפש אפליקציות"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"טוען אפליקציות…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
     <string name="abandoned_search" msgid="891119232568284442">"חפש"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ea9d381..3003cac 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ダブルタップ後に押し続けてウィジェットを選択するか、カスタム操作を使用してください。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"押し続けると、ホーム画面に追加できます"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"自動的に追加"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"アプリを検索"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"アプリを読み込んでいます…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"検索"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 7c41389..8f1edfc 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ორმაგად შეეხეთ და გეჭიროთ ვიჯეტის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ხანგრძლივად შეეხეთ მთავარ ეკრანზე განსათავსებლად"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ავტომატურად განთავსება"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 7762cd7..dfb5aea 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджетті таңдау немесе арнаулы әрекеттерді таңдау үшін екі рет түртіп, ұстап тұрыңыз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Басып тұрып, негізгі экранда қойылатын жерге апарыңыз"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Автоматты қою"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Қолданбаларды іздеу"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Қолданбалар жүктелуде…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» сұрауына сәйкес келетін қолданбалар жоқ"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 862a652..dbfe9d3 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ប៉ះពីរដង ហើយចុចឲ្យជាប់ដើម្បីជ្រើសយកធាតុក្រាហ្វិក ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"សង្កត់​ឲ្យជាប់​ដើម្បី​ដាក់វា​នៅលើ​អេក្រង់​ដើម"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ដាក់​ដោយ​ស្វ័យប្រវត្តិ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"កំពុងដំណើរការកម្មវិធី..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"គ្មានកម្មវិធីដែលត្រូវជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 55fe36c..4817b28 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ವಿಜೆಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ಮುಖಪುಟ ಪರದೆಯ ಮೇಲೆ ಇಡಲು ಟಚ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಇಡಿ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"ಅಪರಿಚಿತ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 85b50c8..5d67857 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"길게 터치하여 홈 화면에 추가"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"자동으로 추가"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"앱 검색"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"앱 로드 중..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'와(과) 일치하는 앱이 없습니다."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
     <string name="abandoned_search" msgid="891119232568284442">"검색"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 75700ce..a43a7eb 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджет тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Басып туруп, башкы экранга сүйрөп барып, таштаңыз"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Автоматтык түрдө жайгаштыруу"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Колдонмолорду издөө"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Колдонмолор жүктөлүүдө…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" дал келген колдонмолор табылган жок"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 31b7db4..2de98dd 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ແຕະ​ຄ້າງ​ໄວ້ ເພື່ອ​ເລືອກວິດ​ເຈັດ ຫຼື ໃຊ້​ການ​ດຳ​ເນີນ​ການ​ກຳ​ນົດ​ເອງ."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ກວ້າງ %1$d ຄູນສູງ %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ແຕະຄ້າງໄວ້ເພື່ອວາງໄວ້ໜ້າຫຼັກ"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ວາງອັດຕະໂນມັດ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ຊອກຫາແອັບ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"​ກຳ​ລັງ​ໂຫລດ​ແອັບ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 1564d45..41298d7 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dukart palieskite ir laikykite, kad pasirinktumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Palieskite ir palaikykite, kad padėtumėte pagrindiniame ekrane"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Padėti automatiškai"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ieškoti programų"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Įkeliamos programos..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Ieškoti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 6458d0b..e623a63 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Lai atlasītu logrīku, veiciet dubultskārienu uz tā un turiet to vai arī veiciet pielāgotas darbības."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Sākuma ekrānā pieskarieties kādai vietai un turiet"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Novietot automātiski"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Meklēt lietotnes"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Notiek lietotņu ielāde…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne."</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 83da8a1..e267c40 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Допрете двапати и задржете за да изберете додаток или да користите приспособени дејства."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d широк на %2$d висок"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Допрете и задржете за да поставите на почетниот екран"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Постави автоматски"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пребарување апликации"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Се вчитуваат апликации…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 0e8c313..37b0f4b 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ഹോം സ്‌ക്രീനിൽ ‌സ്ഥാപിക്കാൻ സ്‌പർശിച്ച് ‌പിടിക്കുക"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"സ്വയമേവ ‌സ്ഥാപിക്കുക"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പുകളെ തിരയുക"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
     <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index d9efe5d..81f41da 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Жижиг хэрэгсэл авах болон тохируулсан үйлдлийг ашиглахын тулд 2 удаа товшоод барина уу."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Нүүр дэлгэцэд байршуулахын тулд дараад, хүлээнэ үү"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Автоматаар байршуулах"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Апп хайх"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Аппликейшныг ачаалж байна..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д нийцэх апп олдсонгүй"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Хайх"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 5938b8e..424d69d 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"मुख्यपृष्ठावर ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"स्वयंचलितपणे ठेवा"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अॅप्स शोधा"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"अॅप्स लोड करीत आहे..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
     <string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 63de9cb..748ebd1 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketik dua kali &amp; tahan untuk mengambil widget atau menggunakan tindakan tersuai"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Sentuh &amp; tahan untuk meletakkan pada skrin utama"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Letakkan secara automatik"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
     <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Tetapan Paparan semasa tidak membenarkan putaran"</string>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index c65f3ab..3f7bf24 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ဝစ်ဂျက်တစ်ခုကိုရယူရန် သို့မဟုတ် စိတ်ကြိုက်လုပ်ဆောင်မှုများကို အသုံးပြုရန် နှစ်ချက်တို့ပြီး ကိုင်ထားပါ။"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ပင်မစာမျက်နှာတွင် ထားရန် ထိပြီး ဖိထားပါ"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"အလိုအလျောက် ထားရန်"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ရှာဖွေမှု အက်ပ်များ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"အက်ပ်များ ရယူနေစဉ်..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 38f027a..21e15be 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dobbelttrykk og hold inne for å velge en modul eller bruke tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Trykk og hold for å plassere på startskjermen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Plassér automatisk"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søk i apper"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Laster inn apper …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index c97eaa2..1012f76 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"विजेटलाई छान्न वा आफू अनुकूल कार्यहरू प्रयोग गर्न डबल ट्याप गरी होल्ड गर्नुहोस्।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"गृह स्क्रिनमा राख्न छुनुहोस् र थिची राख्नुहोस्‌"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"स्वतः राख्नुहोस्"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"अनुप्रयोगहरू लोड गरिँदै..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 90f4b9e..e6c6499 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en blijf aanraken om een widget toe te voegen of aangepaste acties te gebruiken."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Tik op een item en houd dit vast om het op het startscherm te plaatsen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Automatisch plaatsen"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Apps zoeken"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps laden…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 38769aa..719ade8 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਡਬਲ-ਟੈਪ &amp; ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਰੱਖੋ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 1e2fc47..b56a0c0 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Kliknij dwukrotnie i przytrzymaj, by wybrać widżet lub użyć działań niestandardowych."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Kliknij i przytrzymaj, by umieścić na ekranie głównym"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Umieść automatycznie"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Szukaj w aplikacjach"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Wczytuję aplikacje…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 000c7ad..5e4641b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes sem soltar para escolher um widget ou utilize ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Toque sem soltar para colocar no ecrã principal"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Colocar automaticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar aplicações"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"A carregar aplicações..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Não foram encontradas aplic. que correspondam a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 9969997..40919ac 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes e segure para selecionar um widget ou usar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Toque e segure-o para posicionar na tela inicial"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Posicionar automaticamente"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Carregando apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 6faf57f..e629299 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Atingeți de două ori și mențineți apăsat ca să alegeți un widget sau folosiți acțiuni personalizate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Atingeți lung pentru a plasa pe ecranul de pornire"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Plasare automată"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Căutați aplicații"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Se încarcă aplicațiile..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 7bc1819..786f3de 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Чтобы выбрать виджет, нажмите на него дважды и не отпускайте или выполните предложенные действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Нажмите и удерживайте, чтобы добавить на главный экран"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Добавить на главный экран автоматически"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Поиск приложений"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Загрузка…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Удалить"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Найти"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 86f616e..7a450e4 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"විජට් එකක් අහුලා ගැනීමට හෝ අභිරුචි ක්‍රියා කිරීමට ඩබල් ටැප් කර අල්ලා ගෙන සිටින්න."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"මුල් පිටු තිරයෙහි තැබීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"ස්වයංක්‍රියව තබන්න"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"යෙදුම් සෙවීම"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"යෙදුම් පූරණය වෙමින්…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
     <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index d0a2e59..93c1d26 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Klepnutím a podržaním umiestníte na plochu"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Umiestniť automaticky"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hľadať aplikácie"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítavajú sa aplikácie..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0682f8d..5274e4b 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Če želite izbrati pripomoček ali uporabiti dejanja po meri, se ga dvakrat dotaknite in ga pridržite."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dotaknite se elementa in ga pridržite, da ga postavite na začetni zaslon"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Postavi samodejno"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Iskanje po aplikacijah"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Nalaganje aplikacij …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 701eaf8..66a3f1d 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Prek dy herë dhe mbaj shtypur për të zgjedhur një miniaplikacion ose për të përdorur veprimet e personalizuara."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Prek dhe mbaj të shtypur për të vendosur në ekranin bazë"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Vendos automatikisht"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a396f18..5a6df3a 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двапут додирните и задржите да бисте изабрали виџет или користите прилагођене радње."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ширина од %1$d и висина од %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Додирните и задржите да бисте поставили на почетни екран"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Постави аутоматски"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Претражите апликације"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Апликације се учитавају..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Претражи"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bedef80..3730e8f 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryck två gånger och håll kvar om du vill ta upp en widget eller använda anpassade åtgärder."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Placera på startskärmen genom att trycka länge"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Placera automatiskt"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sök efter appar"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Läser in appar …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Det gick inte att hitta några appar som matchar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 20654e0..05540c9 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Gonga mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Gusa na ushikilie ili uweke kwenye skrini ya kwanza"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Weka kiotomatiki"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tafuta Programu"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Inapakia Programu..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Haikupata programu zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -75,6 +77,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index fd51b71..59f29fb 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"முகப்புத் திரையில் சேர்க்க, அதைத் தொட்டுப் பிடித்திருக்கவும்"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"தானாகவே சேர்"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
     <string name="abandoned_search" msgid="891119232568284442">"தேடு"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index ba19ddc..54a78a2 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"విడ్జెట్‌ను ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి, ఉంచండి."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"హోమ్ స్క్రీన్‌లో ఉంచడానికి నొక్కి &amp; పట్టుకోండి"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"స్వయంచాలకంగా ఉంచండి"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"అనువర్తనాలను లోడ్ చేస్తోంది…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
     <string name="abandoned_search" msgid="891119232568284442">"శోధించు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 4bcda7a..109c61f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"แตะ 2 ครั้งค้างไว้เพื่อเลือกวิดเจ็ตหรือใช้การกระทำที่กำหนดเอง"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"กว้าง %1$d x สูง %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"แตะค้างไว้เพื่อวางบนหน้าจอหลัก"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"วางโดยอัตโนมัติ"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ค้นหาแอป"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"กำลังโหลดแอป…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ค้นหา"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 55e02e9..a2f5fcf 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"I-double tap nang matagal upang pumili ng widget o gumamit ng mga custom na pagkilos."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Pindutin nang matagal upang ilagay sa home screen"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Awtomatikong ilagay"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Mga App sa Paghahanap"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Nilo-load ang Mga App…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Walang nakitang Mga App na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Maghanap"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index f30b559..f55331d 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez hafifçe dokunun ve basılı tutun."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Ana ekrana yerleştirmek için dokunun ve basılı tutun"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Otomatik olarak yerleştir"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Uygulamalarda Ara"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Uygulamalar Yükleniyor…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 750b6a0..420b928 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двічі натисніть і утримуйте, щоб вибрати віджет, або виконайте іншу дію."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина – %1$d, висота – %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Натисніть і утримуйте, щоб додати на головний екран"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Додати автоматично"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук додатків"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Завантаження додатків…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Видалити"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукати"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index b285de0..e5c7c58 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"کوئی ویجٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور پکڑے رکھیں۔"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d چوڑا اور ‎%2$d اونچا"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"ہوم اسکرین پر رکھنے کیلئے ٹچ کریں اور دبائے رکھیں"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"خودکار طور پر رکھیں"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ایپس تلاش کریں"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ایپس لوڈ ہو رہی ہیں…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
     <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 77b7367..8e050b8 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Bosh ekranga chiqarish uchun bosib turing"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Avtomatik joylashtirish"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ilovalar ichidan qidirish"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilovalar yuklanmoqda…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” so‘rovi bo‘yicha hech narsa topilmadi"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Qidirish"</string>
diff --git a/res/values-v26/bools.xml b/res/values-v26/bools.xml
index 1093f78..30537fe 100644
--- a/res/values-v26/bools.xml
+++ b/res/values-v26/bools.xml
@@ -18,4 +18,6 @@
 
 <resources>
     <bool name="notification_badging_enabled">true</bool>
+
+    <bool name="enable_install_shortcut_api">false</bool>
 </resources>
\ No newline at end of file
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 2d953e6..462d365 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Nhấn đúp và giữ để chọn tiện ích hoặc sử dụng tác vụ tùy chỉnh."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Chạm và giữ để đặt lên màn hình chính"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Tự động đặt"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tìm kiếm ứng dụng"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Đang tải ứng dụng..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7967812..e54499b 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"点按两次并按住小部件即可选择小部件,您也可以使用自定义操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"宽 %1$d,高 %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"触摸并按住即可放在主屏幕上"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"自动放置"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜索应用"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在加载应用…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜索"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 236fc42..04e1435 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"連扲兩下,然後扲住,就可以新增小工具,或者執行自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"輕觸並按住即可放在主畫面上"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"自動放置"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"無法找到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8d4c744..6a28e4c 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,您也可以使用自訂動作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"按住即可放在主螢幕上"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"自動放置"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"找不到符合「<xliff:g id="QUERY">%1$s</xliff:g>」的應用程式"</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 88d4295..55c3ed6 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -33,6 +33,8 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Thepha kabili bese uyabamba ukuze uthathe iwijethi noma sebenzisa izenzo ezingokwezifiso."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
+    <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Thinta uphinde ubambe ukuze ubeke kusikrini sasekhaya"</string>
+    <string name="place_automatically" msgid="1502491650329146581">"Beka ngokuzenzakalela"</string>
     <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sesha Izinhlelo Zokusebenza"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilayisha izinhlelo zokusebenza..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -73,6 +75,10 @@
     <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>
+    <!-- no translation found for auto_add_shortcuts_label (8222286205987725611) -->
+    <skip />
+    <!-- no translation found for auto_add_shortcuts_description (7117251166066978730) -->
+    <skip />
     <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
diff --git a/res/values/bools.xml b/res/values/bools.xml
index cc4a7ba..53c67e2 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -18,4 +18,6 @@
 
 <resources>
     <bool name="notification_badging_enabled">false</bool>
+
+    <bool name="enable_install_shortcut_api">true</bool>
 </resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ba0c164..e344571 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -31,4 +31,9 @@
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
+    <color name="notification_header_background_color">#F5F5F5</color> <!-- Gray 100 -->
+    <color name="notification_background_color">#FFF</color>
+    <color name="notification_color_beneath">#E0E0E0</color> <!-- Gray 300 -->
+    <color name="divider_color">@color/notification_color_beneath</color>
+    <color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index cb813d5..745bce3 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -72,16 +72,15 @@
          filter the activities shown in the launcher. Can be empty. -->
     <string name="app_filter_class" translatable="false"></string>
 
-    <!-- List of package names that com.android.launcher3.action.LAUNCH
-     should be targeting. Can be empty. -->
-    <array name="launch_broadcast_targets" translatable="false"></array>
-
     <!-- Name of an icon provider class. -->
     <string name="icon_provider_class" translatable="false"></string>
 
     <!-- Name of a drawable factory class. -->
     <string name="drawable_factory_class" translatable="false"></string>
 
+    <!-- Name of a user event dispatcher class. -->
+    <string name="user_event_dispatcher_class" translatable="false"></string>
+
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 517bf9f..132ae07 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -150,10 +150,9 @@
 
 <!-- Deep shortcuts -->
     <dimen name="deep_shortcuts_elevation">9dp</dimen>
-    <dimen name="bg_pill_width">208dp</dimen>
-    <dimen name="bg_pill_height">48dp</dimen>
-    <dimen name="bg_pill_radius">24dp</dimen>
-    <dimen name="deep_shortcuts_spacing">4dp</dimen>
+    <dimen name="bg_popup_item_width">208dp</dimen>
+    <dimen name="bg_popup_item_height">48dp</dimen>
+    <dimen name="popup_items_spacing">4dp</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>
@@ -171,25 +170,35 @@
          deep_shortcut_padding_end + deep_shortcut_drag_handle_size / 2 - deep_shortcuts_arrow_width / 2
          also happens to equal 19dp-->
     <dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
+    <!-- popup_item_width - icon_size - padding_start - drawable_padding -->
+    <dimen name="deep_shortcuts_divider_width">158dp</dimen>
 
 <!-- Icon badges (with notification counts) -->
     <dimen name="badge_size">24dp</dimen>
     <dimen name="badge_text_size">12dp</dimen>
-    <dimen name="badge_small_padding">1dp</dimen>
+    <dimen name="badge_small_padding">0dp</dimen>
     <dimen name="badge_large_padding">3dp</dimen>
-    <dimen name="notification_icon_size">28dp</dimen>
-    <dimen name="notification_footer_icon_size">24dp</dimen>
-    <!-- (icon_size - secondary_icon_size) / 2 -->
+    <dimen name="notification_icon_size">24dp</dimen>
+    <dimen name="notification_footer_icon_size">18dp</dimen>
 
 <!-- Notifications -->
-    <dimen name="notification_footer_icon_row_padding">2dp</dimen>
-    <dimen name="notification_icon_margin_start">8dp</dimen>
-    <dimen name="notification_text_margin_start">8dp</dimen>
-    <dimen name="notification_footer_height">36dp</dimen>
-    <!-- The height to use when there are no icons in the footer -->
-    <dimen name="notification_footer_collapsed_height">@dimen/bg_pill_radius</dimen>
+    <dimen name="bg_round_rect_radius">12dp</dimen>
+    <dimen name="notification_padding">12dp</dimen>
+    <!-- notification_padding + (icon_size - footer_icon_size) / 2 -->
+    <dimen name="notification_footer_icon_row_padding">15dp</dimen>
+    <dimen name="notification_header_padding_after_count">8dp</dimen>
+    <dimen name="notification_header_height">32dp</dimen>
+    <dimen name="notification_main_height">60dp</dimen>
+    <dimen name="notification_footer_height">@dimen/bg_popup_item_height</dimen>
+    <dimen name="notification_header_text_size">12dp</dimen>
+    <dimen name="notification_main_text_size">14dp</dimen>
+    <!-- notification_icon_size + notification+padding + padding we want between icon and text -->
+    <dimen name="notification_main_text_padding_end">40dp</dimen>
     <dimen name="notification_elevation">2dp</dimen>
-    <dimen name="notification_divider_height">0.5dp</dimen>
+    <dimen name="horizontal_ellipsis_size">18dp</dimen>
+    <!-- arrow_horizontal_offset - (ellipsis_size - arrow_width) / 2 -->
+    <dimen name="horizontal_ellipsis_offset">15dp</dimen>
+    <dimen name="popup_item_divider_height">0.5dp</dimen>
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
 <!-- Other -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a6f44f6..423a772 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -53,9 +53,9 @@
     <!-- Accessibility spoken message format for the dimensions of a widget in the drawer -->
     <string name="widget_accessible_dims_format">%1$d wide by %2$d high</string>
     <!-- Message to tell the user to press and hold a widget/icon to add it  -->
-    <string name="add_item_request_drag_hint" translatable="false">Touch &amp; hold to place on home screen</string>
+    <string name="add_item_request_drag_hint">Touch &amp; hold to place on home screen</string>
     <!-- Button label to automatically add icon on home screen [CHAR_LIMIT=50] -->
-    <string name="place_automatically" translatable="false">Place automatically</string>
+    <string name="place_automatically">Place automatically</string>
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
@@ -72,7 +72,10 @@
          The text must fit in the size of a small icon [CHAR_LIMIT=3] -->
     <string name="deep_notifications_overflow" translatable="false">+%1$d</string>
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
-    <string name="notifications_header" translatable="false">Notifications</string>
+    <plurals name="notifications_header" translatable="false">
+        <item quantity="one">Notification</item>
+        <item quantity="other">Notifications</item>
+    </plurals>
 
     <!-- Drag and drop -->
     <skip />
@@ -176,6 +179,11 @@
     <!-- Text explaining that rotation is disabled in Display settings. 'Display' refers to the Display section in system settings [CHAR LIMIT=100] -->
     <string name="allow_rotation_blocked_desc">Current Display setting doesn\'t permit rotation</string>
 
+    <!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] -->
+    <string name="auto_add_shortcuts_label">Add icon to Home screen</string>
+    <!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] -->
+    <string name="auto_add_shortcuts_description">For new apps</string>
+
     <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
     <string name="package_state_unknown">Unknown</string>
 
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 624d9eb..a16583d 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -23,4 +23,11 @@
             android:persistent="true"
     />
 
+    <SwitchPreference
+        android:key="pref_add_icon_to_home"
+        android:title="@string/auto_add_shortcuts_label"
+        android:summary="@string/auto_add_shortcuts_description"
+        android:defaultValue="true"
+        android:persistent="true"
+        />
 </PreferenceScreen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 52a83dc..bd12686 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -95,7 +95,7 @@
         return null;
     }
 
-    protected static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) {
+    public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) {
         AbstractFloatingView view = getOpenView(launcher, type);
         if (view != null) {
             view.close(true);
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 9cce9b1..5b42cad 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -208,16 +208,6 @@
     }
 
     /**
-     * Query the launcher apps service for whether the supplied package has
-     * MAIN/LAUNCHER activities in the supplied package.
-     */
-    static boolean packageHasActivities(Context context, String packageName,
-            UserHandle user) {
-        final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-        return launcherApps.getActivityList(packageName, user).size() > 0;
-    }
-
-    /**
      * Returns whether <em>apps</em> contains <em>component</em>.
      */
     private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 8bf49c2..0ddde73 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -21,14 +21,11 @@
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
-import android.util.Log;
 
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
-import java.util.ArrayList;
-
 /**
  * Represents an app in AllAppsView.
  */
@@ -44,7 +41,7 @@
     /**
      * {@see ShortcutInfo#isDisabled}
      */
-    int isDisabled = ShortcutInfo.DEFAULT;
+    public int isDisabled = ShortcutInfo.DEFAULT;
 
     public AppInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
@@ -83,7 +80,6 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         isDisabled = info.isDisabled;
-        iconBitmap = info.iconBitmap;
     }
 
     @Override
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 7bc3692..84a8bce 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -83,7 +83,7 @@
 
         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
         if (app != null) {
-            app.reloadWorkspace();
+            app.getModel().forceReload();
         }
         asyncResult.finish();
     }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 9f4f1f9..e1a3ad0 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -21,9 +21,12 @@
 import android.content.ContextWrapper;
 import android.view.View.AccessibilityDelegate;
 
+import com.android.launcher3.logging.UserEventDispatcher;
+
 public abstract class BaseActivity extends Activity {
 
     protected DeviceProfile mDeviceProfile;
+    protected UserEventDispatcher mUserEventDispatcher;
 
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -33,6 +36,13 @@
         return null;
     }
 
+    public final UserEventDispatcher getUserEventDispatcher() {
+        if (mUserEventDispatcher == null) {
+            mUserEventDispatcher = UserEventDispatcher.get(this);
+        }
+        return mUserEventDispatcher;
+    }
+
     public static BaseActivity fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (BaseActivity) context;
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index ba7c3f8..5feb42e 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -36,10 +36,6 @@
  */
 public class BaseRecyclerViewFastScrollBar {
 
-    public interface FastScrollFocusableView {
-        void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
-    }
-
     private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
             new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1a41e08..f9a6742 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -40,11 +40,12 @@
 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;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.text.NumberFormat;
 
@@ -53,8 +54,7 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView
-        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
 
     // Dimensions in DP
     private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
@@ -67,6 +67,8 @@
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
 
+    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
+
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final boolean mCenterVertically;
@@ -232,14 +234,21 @@
     }
 
     @Override
-    public void setPressed(boolean pressed) {
-        super.setPressed(pressed);
-
+    public void refreshDrawableState() {
         if (!mIgnorePressedStateChange) {
-            updateIconState();
+            super.refreshDrawableState();
         }
     }
 
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        if (mStayPressed) {
+            mergeDrawableStates(drawableState, STATE_PRESSED);
+        }
+        return drawableState;
+    }
+
     /** Returns the icon for this view. */
     public Drawable getIcon() {
         return mIcon;
@@ -250,17 +259,6 @@
         return mLayoutHorizontal;
     }
 
-    private void updateIconState() {
-        if (mIcon instanceof FastBitmapDrawable) {
-            FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
-            if (isPressed() || mStayPressed) {
-                d.animateState(FastBitmapDrawable.State.PRESSED);
-            } else {
-                d.animateState(FastBitmapDrawable.State.NORMAL);
-            }
-        }
-    }
-
     @Override
     public void setOnLongClickListener(OnLongClickListener l) {
         super.setOnLongClickListener(l);
@@ -334,7 +332,7 @@
                     this, mPressedBackground);
         }
 
-        updateIconState();
+        refreshDrawableState();
     }
 
     void clearPressedBackground() {
@@ -364,7 +362,7 @@
 
         mPressedBackground = null;
         mIgnorePressedStateChange = false;
-        updateIconState();
+        refreshDrawableState();
         return result;
     }
 
@@ -505,15 +503,14 @@
         if (mIcon instanceof FastBitmapDrawable) {
             BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
             BadgeRenderer badgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
+            PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(mLauncher);
+            if (popup != null) {
+                popup.updateNotificationHeader(badgeInfo, itemInfo);
+            }
             ((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer, animate);
         }
     }
 
-    public IconPalette getIconPalette() {
-        return mIcon instanceof FastBitmapDrawable ? ((FastBitmapDrawable) mIcon).getIconPalette()
-                : null;
-    }
-
     /**
      * Sets the icon for this view based on the layout direction.
      */
@@ -544,10 +541,6 @@
     @Override
     public void reapplyItemInfo(ItemInfoWithIcon info) {
         if (getTag() == info) {
-            FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
-            if (mIcon instanceof FastBitmapDrawable) {
-                prevState = ((FastBitmapDrawable) mIcon).getCurrentState();
-            }
             mIconLoadRequest = null;
             mDisableRelayout = true;
 
@@ -555,7 +548,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) {
@@ -566,12 +561,6 @@
                 applyFromPackageItemInfo((PackageItemInfo) info);
             }
 
-            // If we are reapplying over an old icon, then we should update the new icon to the same
-            // state as the old icon
-            if (mIcon instanceof FastBitmapDrawable) {
-                ((FastBitmapDrawable) mIcon).setState(prevState);
-            }
-
             mDisableRelayout = false;
         }
     }
@@ -593,34 +582,6 @@
         }
     }
 
-    @Override
-    public void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated) {
-        // We can only set the fast scroll focus state on a FastBitmapDrawable
-        if (!(mIcon instanceof FastBitmapDrawable)) {
-            return;
-        }
-
-        FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
-        if (animated) {
-            FastBitmapDrawable.State prevState = d.getCurrentState();
-            if (d.animateState(focusState)) {
-                // If the state was updated, then update the view accordingly
-                animate().scaleX(focusState.viewScale)
-                        .scaleY(focusState.viewScale)
-                        .setStartDelay(getStartDelayForStateChange(prevState, focusState))
-                        .setDuration(d.getDurationForStateChange(prevState, focusState))
-                        .start();
-            }
-        } else {
-            if (d.setState(focusState)) {
-                // If the state was updated, then update the view accordingly
-                animate().cancel();
-                setScaleX(focusState.viewScale);
-                setScaleY(focusState.viewScale);
-            }
-        }
-    }
-
     /**
      * Returns true if the view can show custom shortcuts.
      */
@@ -629,19 +590,8 @@
                 .isEmpty();
     }
 
-    /**
-     * Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
-     */
-    private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
-            final FastBitmapDrawable.State toState) {
-        switch (toState) {
-            case NORMAL:
-                switch (fromState) {
-                    case FAST_SCROLL_HIGHLIGHTED:
-                        return FastBitmapDrawable.FAST_SCROLL_INACTIVE_DURATION / 4;
-                }
-        }
-        return 0;
+    public int getIconSize() {
+        return mIconSize;
     }
 
     /**
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8d69fe3..8a477d8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 /**
@@ -142,8 +143,8 @@
             mCurrentFilter = new ColorMatrix();
         }
 
-        DragView.setColorScale(getTextColor(), mSrcFilter);
-        DragView.setColorScale(targetColor, mDstFilter);
+        Themes.setColorScaleOnMatrix(getTextColor(), mSrcFilter);
+        Themes.setColorScaleOnMatrix(targetColor, mDstFilter);
         ValueAnimator anim1 = ValueAnimator.ofObject(
                 new FloatArrayEvaluator(mCurrentFilter.getArray()),
                 mSrcFilter.getArray(), mDstFilter.getArray());
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index cac6c06..c0946a0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -51,7 +51,7 @@
 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.graphics.DragPreviewProvider;
 import com.android.launcher3.util.CellAndSpan;
@@ -106,7 +106,6 @@
 
     private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
     FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
-    Paint mFolderBgPaint = new Paint();
 
     private float mBackgroundAlpha;
 
@@ -502,9 +501,9 @@
             cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
-            bg.drawBackground(canvas, mFolderBgPaint);
+            bg.drawBackground(canvas);
             if (!bg.isClipping) {
-                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
+                bg.drawBackgroundStroke(canvas);
             }
             canvas.restore();
         }
@@ -514,7 +513,7 @@
                     mFolderLeaveBehind.delegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
-            mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
+            mFolderLeaveBehind.drawLeaveBehind(canvas);
             canvas.restore();
         }
     }
@@ -529,7 +528,7 @@
                 cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
                 canvas.save();
                 canvas.translate(mTempLocation[0], mTempLocation[1]);
-                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
+                bg.drawBackgroundStroke(canvas);
                 canvas.restore();
             }
         }
@@ -570,7 +569,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.
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/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c9e3d4f..43f7d23 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -32,6 +32,7 @@
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.ArrayList;
 
@@ -530,10 +531,13 @@
                 workspacePadding.bottom);
         workspace.setPageSpacing(getWorkspacePageSpacing());
 
-        View qsbContainer = launcher.getQsbContainer();
-        lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
-        lp.topMargin = mInsets.top + workspacePadding.top;
-        qsbContainer.setLayoutParams(lp);
+        // 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);
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index d05673c..596aa8f 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -29,6 +29,7 @@
 public class ExtendedEditText extends EditText {
 
     private boolean mShowImeAfterFirstLayout;
+    private boolean mForceDisableSuggestions = false;
 
     /**
      * Implemented by listeners of the back key.
@@ -107,4 +108,17 @@
             mBackKeyListener.onBackKey();
         }
     }
+
+    /**
+     * Set to true when you want isSuggestionsEnabled to return false.
+     * Use this to disable the red underlines that appear under typos when suggestions is enabled.
+     */
+    public void forceDisableSuggestions(boolean forceDisableSuggestions) {
+        mForceDisableSuggestions = forceDisableSuggestions;
+    }
+
+    @Override
+    public boolean isSuggestionsEnabled() {
+        return !mForceDisableSuggestions && super.isSuggestionsEnabled();
+    }
 }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 1f74c88..5a44f75 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.graphics.Bitmap;
@@ -32,42 +31,19 @@
 import android.graphics.drawable.Drawable;
 import android.util.Property;
 import android.util.SparseArray;
-import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.graphics.IconPalette;
 
 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;
 
-    /**
-     * The possible states that a FastBitmapDrawable can be in.
-     */
-    public enum State {
-
-        NORMAL                      (0f, 0f, 1f, new DecelerateInterpolator()),
-        PRESSED                     (0f, 100f / 255f, 1f, CLICK_FEEDBACK_INTERPOLATOR),
-        FAST_SCROLL_HIGHLIGHTED     (0f, 0f, 1.15f, new DecelerateInterpolator()),
-        FAST_SCROLL_UNHIGHLIGHTED   (0f, 0f, 1f, new DecelerateInterpolator());
-
-        public final float desaturation;
-        public final float brightness;
-        /**
-         * Used specifically by the view drawing this FastBitmapDrawable.
-         */
-        public final float viewScale;
-        public final TimeInterpolator interpolator;
-
-        State(float desaturation, float brightness, float viewScale, TimeInterpolator interpolator) {
-            this.desaturation = desaturation;
-            this.brightness = brightness;
-            this.viewScale = viewScale;
-            this.interpolator = interpolator;
-        }
-    }
-
     public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
 
         @Override
@@ -82,10 +58,6 @@
         }
     };
     public static final int CLICK_FEEDBACK_DURATION = 2000;
-    public static final int FAST_SCROLL_HIGHLIGHT_DURATION = 225;
-    public static final int FAST_SCROLL_UNHIGHLIGHT_DURATION = 150;
-    public static final int FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION = 225;
-    public static final int FAST_SCROLL_INACTIVE_DURATION = 275;
 
     // Since we don't need 256^2 values for combinations of both the brightness and saturation, we
     // reduce the value space to a smaller value V, which reduces the number of cached
@@ -101,7 +73,8 @@
 
     protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
     private final Bitmap mBitmap;
-    private State mState = State.NORMAL;
+
+    private boolean mIsPressed;
     private boolean mIsDisabled;
 
     private BadgeInfo mBadgeInfo;
@@ -123,6 +96,19 @@
         }
     };
 
+    private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
+            = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
+        @Override
+        public Float get(FastBitmapDrawable fastBitmapDrawable) {
+            return fastBitmapDrawable.getBrightness();
+        }
+
+        @Override
+        public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
+            fastBitmapDrawable.setBrightness(value);
+        }
+    };
+
     // The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
     // as a result, can be used to compose the key for the cached ColorMatrixColorFilters
     private int mDesaturation = 0;
@@ -130,8 +116,8 @@
     private int mAlpha = 255;
     private int mPrevUpdateKey = Integer.MAX_VALUE;
 
-    // Animators for the fast bitmap drawable's properties
-    private AnimatorSet mPropertyAnimator;
+    // Animators for the fast bitmap drawable's brightness
+    private ObjectAnimator mBrightnessAnimator;
 
     public FastBitmapDrawable(Bitmap b) {
         mBitmap = b;
@@ -181,7 +167,7 @@
         }
     }
 
-    public IconPalette getIconPalette() {
+    protected IconPalette getIconPalette() {
         if (mIconPalette == null) {
             mIconPalette = IconPalette.fromDominantColor(Utilities
                     .findDominantColorByHue(mBitmap, 20));
@@ -243,60 +229,50 @@
         return mBitmap;
     }
 
-    /**
-     * Animates this drawable to a new state.
-     *
-     * @return whether the state has changed.
-     */
-    public boolean animateState(State newState) {
-        State prevState = mState;
-        if (mState != newState) {
-            mState = newState;
-
-            float desaturation = mIsDisabled ? DISABLED_DESATURATION : newState.desaturation;
-            float brightness = mIsDisabled ? DISABLED_BRIGHTNESS: newState.brightness;
-
-            mPropertyAnimator = cancelAnimator(mPropertyAnimator);
-            mPropertyAnimator = new AnimatorSet();
-            mPropertyAnimator.playTogether(
-                    ObjectAnimator.ofFloat(this, "desaturation", desaturation),
-                    ObjectAnimator.ofFloat(this, "brightness", brightness));
-            mPropertyAnimator.setInterpolator(newState.interpolator);
-            mPropertyAnimator.setDuration(getDurationForStateChange(prevState, newState));
-            mPropertyAnimator.setStartDelay(getStartDelayForStateChange(prevState, newState));
-            mPropertyAnimator.start();
-            return true;
-        }
-        return false;
+    @Override
+    public boolean isStateful() {
+        return true;
     }
 
-    /**
-     * Immediately sets this drawable to a new state.
-     *
-     * @return whether the state has changed.
-     */
-    public boolean setState(State newState) {
-        if (mState != newState) {
-            mState = newState;
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean isPressed = false;
+        for (int s : state) {
+            if (s == android.R.attr.state_pressed) {
+                isPressed = true;
+                break;
+            }
+        }
+        if (mIsPressed != isPressed) {
+            mIsPressed = isPressed;
 
-            mPropertyAnimator = cancelAnimator(mPropertyAnimator);
+            if (mBrightnessAnimator != null) {
+                mBrightnessAnimator.cancel();
+            }
 
-            invalidateDesaturationAndBrightness();
+            if (mIsPressed) {
+                // Animate when going to pressed state
+                mBrightnessAnimator = ObjectAnimator.ofFloat(
+                        this, BRIGHTNESS, getExpectedBrightness());
+                mBrightnessAnimator.setDuration(CLICK_FEEDBACK_DURATION);
+                mBrightnessAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
+                mBrightnessAnimator.start();
+            } else {
+                setBrightness(getExpectedBrightness());
+            }
             return true;
         }
         return false;
     }
 
     private void invalidateDesaturationAndBrightness() {
-        setDesaturation(mIsDisabled ? DISABLED_DESATURATION : mState.desaturation);
-        setBrightness(mIsDisabled ? DISABLED_BRIGHTNESS: mState.brightness);
+        setDesaturation(mIsDisabled ? DISABLED_DESATURATION : 0);
+        setBrightness(getExpectedBrightness());
     }
 
-    /**
-     * Returns the current state.
-     */
-    public State getCurrentState() {
-        return mState;
+    private float getExpectedBrightness() {
+        return mIsDisabled ? DISABLED_BRIGHTNESS :
+                (mIsPressed ? PRESSED_BRIGHTNESS : 0);
     }
 
     public void setIsDisabled(boolean isDisabled) {
@@ -307,49 +283,6 @@
     }
 
     /**
-     * Returns the duration for the state change animation.
-     */
-    public static int getDurationForStateChange(State fromState, State toState) {
-        switch (toState) {
-            case NORMAL:
-                switch (fromState) {
-                    case PRESSED:
-                        return 0;
-                    case FAST_SCROLL_HIGHLIGHTED:
-                    case FAST_SCROLL_UNHIGHLIGHTED:
-                        return FAST_SCROLL_INACTIVE_DURATION;
-                }
-            case PRESSED:
-                return CLICK_FEEDBACK_DURATION;
-            case FAST_SCROLL_HIGHLIGHTED:
-                return FAST_SCROLL_HIGHLIGHT_DURATION;
-            case FAST_SCROLL_UNHIGHLIGHTED:
-                switch (fromState) {
-                    case NORMAL:
-                        // When animating from normal state, take a little longer
-                        return FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION;
-                    default:
-                        return FAST_SCROLL_UNHIGHLIGHT_DURATION;
-                }
-        }
-        return 0;
-    }
-
-    /**
-     * Returns the start delay when animating between certain fast scroll states.
-     */
-    public static int getStartDelayForStateChange(State fromState, State toState) {
-        switch (toState) {
-            case FAST_SCROLL_UNHIGHLIGHTED:
-                switch (fromState) {
-                    case NORMAL:
-                        return FAST_SCROLL_UNHIGHLIGHT_DURATION / 4;
-                }
-        }
-        return 0;
-    }
-
-    /**
      * Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
      */
     private void setDesaturation(float desaturation) {
@@ -435,11 +368,4 @@
         }
         invalidateSelf();
     }
-
-    private AnimatorSet cancelAnimator(AnimatorSet animator) {
-        if (animator != null) {
-            animator.cancel();
-        }
-        return null;
-    }
 }
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 2c69d0a..0041bb4 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -114,11 +114,18 @@
         }
     }
 
+    public void prepareAutoUpdate() {
+        for (int i = 0; i < listeners.size(); i++) {
+            listeners.get(i).prepareAutoUpdate();
+        }
+    }
+
     public interface FolderListener {
         public void onAdd(ShortcutInfo item);
         public void onRemove(ShortcutInfo item);
         public void onTitleChanged(CharSequence title);
         public void onItemsChanged(boolean animate);
+        public void prepareAutoUpdate();
     }
 
     public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 1bab774..80dec16 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -218,6 +218,10 @@
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
     }
 
+    public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
+    }
+
     public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
         HashSet<ShortcutKey> result = new HashSet<>();
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 8aeab87..146c2ee 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.logging.FileLog;
 import com.android.launcher3.util.Thunk;
 
@@ -335,7 +334,7 @@
     }
 
     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/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index a3d8c6a..1e020e2 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -35,7 +35,9 @@
 
     protected ItemInfoWithIcon() { }
 
-    protected ItemInfoWithIcon(ItemInfo info) {
+    protected ItemInfoWithIcon(ItemInfoWithIcon info) {
         super(info);
+        iconBitmap = info.iconBitmap;
+        usingLowResIcon = info.usingLowResIcon;
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c5cefa6..a69f501 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -72,6 +72,7 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.OvershootInterpolator;
@@ -86,15 +87,11 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PinItemRequestCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -107,10 +104,13 @@
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -120,7 +120,6 @@
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LogConfig;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -176,6 +175,13 @@
      */
     protected static final int REQUEST_LAST = 100;
 
+    private static final int SOFT_INPUT_MODE_DEFAULT =
+            WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
+                | WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+    private static final int SOFT_INPUT_MODE_ALL_APPS =
+            WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+                | WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+
     // The Intent extra that defines whether to ignore the launch animation
     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
@@ -316,8 +322,6 @@
      */
     private PendingRequestArgs mPendingRequestArgs;
 
-    private UserEventDispatcher mUserEventDispatcher;
-
     private float mLastDispatchTouchEventX = 0.0f;
 
     public ViewGroupFocusHelper mFocusHandler;
@@ -628,23 +632,6 @@
         }
     }
 
-    public UserEventDispatcher getUserEventDispatcher() {
-        if (mLauncherCallbacks != null) {
-            UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
-            if (dispatcher != null) {
-                return dispatcher;
-            }
-        }
-
-        // Logger object is a singleton and does not have to be coupled with the foreground
-        // activity. Since most user event logging is done on the UI, the object is retrieved
-        // from the callback for convenience.
-        if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = new UserEventDispatcher();
-        }
-        return mUserEventDispatcher;
-    }
-
     public boolean isDraggingEnabled() {
         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
         // that is subsequently removed from the workspace in startBinding().
@@ -1072,6 +1059,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
+
     }
 
     @Override
@@ -1164,9 +1152,10 @@
         if (mLauncherCallbacks != null) {
             return mLauncherCallbacks.hasSettings();
         } else {
-            // On devices with a locked orientation, we will at least have the allow rotation
-            // setting.
-            return !getResources().getBoolean(R.bool.allow_rotation);
+            // On O and above we there is always some setting present settings (add icon to
+            // home screen or icon badging). On earlier APIs we will have the allow rotation
+            // setting, on devices with a locked orientation,
+            return Utilities.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
         }
     }
 
@@ -2472,7 +2461,7 @@
             throw new IllegalArgumentException("Input must have a valid intent");
         }
         boolean success = startActivitySafely(v, intent, item);
-        getUserEventDispatcher().logAppLaunch(v, intent);
+        getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
 
         if (success && v instanceof BubbleTextView) {
             mWaitingForResume = (BubbleTextView) v;
@@ -2721,9 +2710,10 @@
             intent.setSourceBounds(getViewBounds(v));
         }
         try {
-            if (Utilities.ATLEAST_MARSHMALLOW && item != null
+            if (Utilities.ATLEAST_MARSHMALLOW
+                    && (item instanceof ShortcutInfo)
                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                     || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                     && !((ShortcutInfo) item).isPromise()) {
                 // Shortcuts need some special checks due to legacy reasons.
                 startShortcutIntentSafely(intent, optsBundle, item);
@@ -2896,7 +2886,7 @@
         }
 
         // Change the state *after* we've called all the transition code
-        mState = State.WORKSPACE;
+        setState(State.WORKSPACE);
 
         if (changed) {
             // Send an accessibility event to announce the context change
@@ -2933,12 +2923,30 @@
         mWorkspace.setVisibility(View.VISIBLE);
         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
                 Workspace.State.OVERVIEW, animated, postAnimRunnable);
-        mState = State.WORKSPACE;
+        setState(State.WORKSPACE);
+
         // If animated from long press, then don't allow any of the controller in the drag
         // layer to intercept any remaining touch.
         mWorkspace.requestDisallowInterceptTouchEvent(animated);
     }
 
+    private void setState(State state) {
+        this.mState = state;
+        updateSoftInputMode();
+    }
+
+    private void updateSoftInputMode() {
+        if (FeatureFlags.LAUNCHER3_UPDATE_SOFT_INPUT_MODE) {
+            final int mode;
+            if (isAppsViewVisible()) {
+                mode = SOFT_INPUT_MODE_ALL_APPS;
+            } else {
+                mode = SOFT_INPUT_MODE_DEFAULT;
+            }
+            getWindow().setSoftInputMode(mode);
+        }
+    }
+
     /**
      * Shows the apps view.
      */
@@ -3000,7 +3008,7 @@
         }
 
         // Change the state *after* we've called all the transition code
-        mState = toState;
+        setState(toState);
         AbstractFloatingView.closeAllOpenViews(this);
 
         // Send an accessibility event to announce the context change
@@ -3030,7 +3038,7 @@
         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
                 Workspace.State.SPRING_LOADED, true /* animated */,
                 null /* onCompleteRunnable */);
-        mState = State.WORKSPACE_SPRING_LOADED;
+        setState(State.WORKSPACE_SPRING_LOADED);
     }
 
     public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
@@ -3210,6 +3218,9 @@
         if (LauncherAppState.PROFILE_STARTUP) {
             Trace.beginSection("Starting page bind");
         }
+
+        AbstractFloatingView.closeAllOpenViews(this);
+
         setWorkspaceLoading(true);
 
         // Clear the workspace because it's going to be rebound
@@ -3376,7 +3387,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);
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 43cf827..aa7f5ee 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -130,17 +130,4 @@
         return anim;
     }
 
-    public static ValueAnimator animateViewHeight(final View v, int fromHeight, int toHeight) {
-        ValueAnimator anim = ValueAnimator.ofInt(fromHeight, toHeight);
-        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                int val = (Integer) valueAnimator.getAnimatedValue();
-                ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
-                layoutParams.height = val;
-                v.setLayoutParams(layoutParams);
-            }
-        });
-        return anim;
-    }
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index e0e53a6..f2d66fe 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.model.GridSizeMigrationTask;
 import com.android.launcher3.util.ConfigMonitor;
@@ -38,7 +38,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;
@@ -134,15 +134,6 @@
         PackageInstallerCompat.getInstance(mContext).onStop();
     }
 
-    /**
-     * Reloads the workspace items from the DB and re-binds the workspace. This should generally
-     * not be called as DB updates are automatically followed by UI update
-     */
-    public void reloadWorkspace() {
-        mModel.resetLoadedState(false, true);
-        mModel.startLoaderFromBackground();
-    }
-
     LauncherModel setLauncher(Launcher launcher) {
         getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 6394b90..2bac11f 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -92,7 +92,6 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
-    public UserEventDispatcher getUserEventDispatcher();
     public boolean shouldMoveToDefaultScreenOnHomeIntent();
     public boolean hasSettings();
     public AllAppsSearchBarController getAllAppsSearchBarController();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 34d576d..35811d3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -45,10 +44,11 @@
 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.config.FeatureFlags;
 import com.android.launcher3.dynamicui.ExtractionUtils;
 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.model.AddWorkspaceItemsTask;
@@ -72,8 +72,7 @@
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LooperIdleLock;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -85,7 +84,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -95,6 +93,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 
 /**
@@ -112,9 +111,9 @@
     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 boolean mIsLoaderTaskRunning;
     @Thunk boolean mHasLoaderCompletedOnce;
@@ -125,12 +124,16 @@
     }
     @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
 
-    // We start off with everything not loaded.  After that, we assume that
+    // Indicates whether the current model data is valid or not.
+    // We start off with everything not loaded. After that, we assume that
     // our monitoring of the package manager provides all updates and we never
-    // need to do a requery.  These are only ever touched from the loader thread.
-    private boolean mWorkspaceLoaded;
-    private boolean mAllAppsLoaded;
-    private boolean mDeepShortcutsLoaded;
+    // need to do a requery. This is only ever touched from the loader thread.
+    private boolean mModelLoaded;
+    public boolean isModelLoaded() {
+        synchronized (mLock) {
+            return mModelLoaded && mLoaderTask == null;
+        }
+    }
 
     /**
      * Set of runnables to be called on the background thread after the workspace binding
@@ -150,11 +153,11 @@
     private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
         @Override
         public void run() {
-            if (mDeepShortcutsLoaded) {
+            if (mModelLoaded) {
                 boolean hasShortcutHostPermission =
                         DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
                 if (hasShortcutHostPermission != mHasShortcutHostPermission) {
-                    mApp.reloadWorkspace();
+                    forceReload();
                 }
             }
         }
@@ -216,17 +219,6 @@
         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
      * posted on the worker thread handler. */
     private static void runOnWorkerThread(Runnable r) {
@@ -376,8 +368,6 @@
     public void initialize(Callbacks callbacks) {
         synchronized (mLock) {
             Preconditions.assertUIThread();
-            // Remove any queued UI runnables
-            mHandler.cancelAll();
             mCallbacks = new WeakReference<>(callbacks);
         }
     }
@@ -482,8 +472,16 @@
         }
     }
 
-    void forceReload() {
-        resetLoadedState(true, true);
+    /**
+     * Reloads the workspace items from the DB and re-binds the workspace. This should generally
+     * not be called as DB updates are automatically followed by UI update
+     */
+    public void forceReload() {
+        synchronized (mLock) {
+            // Stop any existing loaders first, so they don't set mModelLoaded to true later
+            stopLoaderLocked();
+            mModelLoaded = false;
+        }
 
         // Do this here because if the launcher activity is running it will be restarted.
         // If it's not running startLoaderFromBackground will merely tell it that it needs
@@ -491,19 +489,6 @@
         startLoaderFromBackground();
     }
 
-    public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) {
-        synchronized (mLock) {
-            // Stop any existing loaders first, so they don't set mAllAppsLoaded or
-            // mWorkspaceLoaded to true later
-            stopLoaderLocked();
-            if (resetAllAppsLoaded) mAllAppsLoaded = false;
-            if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
-            // Always reset deep shortcuts loaded.
-            // TODO: why?
-            mDeepShortcutsLoaded = false;
-        }
-    }
-
     /**
      * When the launcher is in the background, it's possible for it to miss paired
      * configuration changes.  So whenever we trigger the loader from the background
@@ -546,18 +531,17 @@
             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);
-                // TODO: mDeepShortcutsLoaded does not need to be true for synchronous bind.
-                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded
-                        && mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {
+                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
+                        && mModelLoaded && !mIsLoaderTaskRunning) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                     return true;
                 } else {
@@ -602,68 +586,21 @@
 
         @Thunk boolean mIsLoadingAndBindingWorkspace;
         private boolean mStopped;
-        @Thunk boolean mLoadAndBindStepFinished;
 
         LoaderTask(Context context, int pageToBindFirst) {
             mContext = context;
             mPageToBindFirst = pageToBindFirst;
         }
 
-        private void loadAndBindWorkspace() {
-            mIsLoadingAndBindingWorkspace = true;
-
-            // Load the workspace
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
-            }
-
-            if (!mWorkspaceLoaded) {
-                loadWorkspace();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mWorkspaceLoaded = true;
-                }
-            }
-
-            // Bind the workspace
-            bindWorkspace(mPageToBindFirst);
-        }
-
         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();
-                            }
-                        }
-                    });
-
-                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");
-                }
+                LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
+                // Just in case mFlushingWorkerThread changes but we aren't woken up,
+                // wait no longer than 1sec at a time
+                while (!mStopped && idleLock.awaitLocked(1000));
             }
         }
 
@@ -673,7 +610,7 @@
                 throw new RuntimeException("Should not call runBindSynchronousPage() without " +
                         "valid page index");
             }
-            if (!mAllAppsLoaded || !mWorkspaceLoaded) {
+            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");
@@ -686,15 +623,6 @@
                 }
             }
 
-            // 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);
@@ -705,6 +633,14 @@
             bindDeepShortcuts();
         }
 
+        private void verifyNotStopped() throws CancellationException {
+            synchronized (LoaderTask.this) {
+                if (mStopped) {
+                    throw new CancellationException("Loader stopped");
+                }
+            }
+        }
+
         public void run() {
             synchronized (mLock) {
                 if (mStopped) {
@@ -712,41 +648,71 @@
                 }
                 mIsLoaderTaskRunning = true;
             }
-            // Optimize for end-user experience: if the Launcher is up and // running with the
-            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
-            // workspace first (default).
-            keep_running: {
-                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
-                loadAndBindWorkspace();
 
-                if (mStopped) {
-                    break keep_running;
+            try {
+                long now = 0;
+                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");
+                    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: loading all apps");
-                loadAndBindAllApps();
+                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");
+                    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: loading deep shortcuts");
-                loadAndBindDeepShortcuts();
-            }
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+                loadDeepShortcuts();
 
-            // Clear out this reference, otherwise we end up holding it until all of the
-            // callback runnables are done.
-            mContext = null;
+                verifyNotStopped();
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+                bindDeepShortcuts();
 
-            synchronized (mLock) {
-                // If we are still the last one to be scheduled, remove ourselves.
-                if (mLoaderTask == this) {
-                    mLoaderTask = null;
+                synchronized (mLock) {
+                    // Everything loaded bind the data.
+                    mModelLoaded = true;
+                    mHasLoaderCompletedOnce = true;
                 }
-                mIsLoaderTaskRunning = false;
-                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;
+                }
             }
         }
 
@@ -818,7 +784,7 @@
             if (clearDb) {
                 Log.d(TAG, "loadWorkspace: resetting launcher database");
                 LauncherSettings.Settings.call(contentResolver,
-                        LauncherSettings.Settings.METHOD_DELETE_DB);
+                        LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
             }
 
             Log.d(TAG, "loadWorkspace: loading default favorites");
@@ -886,6 +852,8 @@
                     Intent intent;
                     String targetPkg;
 
+                    FolderIconPreviewVerifier verifier =
+                            new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
                     while (!mStopped && c.moveToNext()) {
                         try {
                             if (c.user == null) {
@@ -984,7 +952,7 @@
                                             c.markDeleted("Unrestored app removed: " + targetPkg);
                                             continue;
                                         }
-                                    } else if (pmHelper.isAppOnSdcard(targetPkg)) {
+                                    } 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.
@@ -1010,7 +978,7 @@
                                 }
 
                                 boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
-                                        c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
+                                        !verifier.isItemInPreview(c.getInt(rankIndex));
 
                                 if (c.restoreFlag != 0) {
                                     // Already verified above that user is same as default user
@@ -1034,6 +1002,10 @@
                                         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.
@@ -1044,7 +1016,7 @@
                                     info = c.loadSimpleShortcut();
 
                                     // Shortcuts are only available on the primary profile
-                                    if (pmHelper.isAppSuspended(targetPkg)) {
+                                    if (pmHelper.isAppSuspended(targetPkg, c.user)) {
                                         disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                                     }
 
@@ -1258,17 +1230,23 @@
                     }
                 }
 
-                // Sort all the folder items and make sure the first 3 items are high resolution.
+                FolderIconPreviewVerifier verifier =
+                        new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+                // Sort the folder items and make sure all items in the preview are high resolution.
                 for (FolderInfo folder : sBgDataModel.folders) {
                     Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
-                    int pos = 0;
+                    verifier.setFolderInfo(folder);
+
+                    int numItemsInPreview = 0;
                     for (ShortcutInfo info : folder.contents) {
-                        if (info.usingLowResIcon &&
-                                info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                        if (info.usingLowResIcon
+                                && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                                && verifier.isItemInPreview(info.rank)) {
                             mIconCache.getTitleAndIcon(info, false);
+                            numItemsInPreview++;
                         }
-                        pos ++;
-                        if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+
+                        if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
                             break;
                         }
                     }
@@ -1393,7 +1371,7 @@
                                 return Utilities.longCompare(lhs.screenId, rhs.screenId);
                             }
                             default:
-                                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                                if (FeatureFlags.IS_DOGFOOD_BUILD) {
                                     throw new RuntimeException("Unexpected container type when " +
                                             "sorting workspace items.");
                                 }
@@ -1418,7 +1396,7 @@
                     }
                 }
             };
-            runOnMainThread(r);
+            mUiExecutor.execute(r);
         }
 
         private void bindWorkspaceItems(final Callbacks oldCallbacks,
@@ -1524,11 +1502,11 @@
                     }
                 }
             };
-            runOnMainThread(r);
+            mUiExecutor.execute(r);
 
             bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
 
-            Executor mainExecutor = new DeferredMainThreadExecutor();
+            Executor mainExecutor = mUiExecutor;
             // Load items on the current page.
             bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
 
@@ -1538,7 +1516,7 @@
             // 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;
+                    validFirstPage ? new ViewOnDrawExecutor(mUiExecutor) : mainExecutor;
 
             mainExecutor.execute(new Runnable() {
                 @Override
@@ -1598,30 +1576,7 @@
                         }
                     }
                 };
-                runOnMainThread(r);
-            }
-        }
-
-        private void loadAndBindAllApps() {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
-            }
-            if (!mAllAppsLoaded) {
-                loadAllApps();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                }
-                updateIconCache();
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mAllAppsLoaded = true;
-                }
-            } else {
-                onlyBindAllApps();
+                mUiExecutor.execute(r);
             }
         }
 
@@ -1671,7 +1626,7 @@
                     }
                 }
             };
-            runOnMainThread(r);
+            mUiExecutor.execute(r);
         }
 
         private void loadAllApps() {
@@ -1719,21 +1674,21 @@
                             heuristic.processUserApps(apps);
                         }
                     };
-                    runOnMainThread(new Runnable() {
+                    mUiExecutor.execute(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(r);
-                                }
-                            } else {
-                                runOnWorkerThread(r);
-                            }
-                        }
-                    });
+                                    @Override
+                                    public void run() {
+                                        // Check isLoadingWorkspace on the UI thread, as it is updated on
+                                        // the UI thread.
+                                        if (mIsLoadingAndBindingWorkspace) {
+                                            synchronized (mBindCompleteRunnables) {
+                                                mBindCompleteRunnables.add(r);
+                                            }
+                                        } else {
+                                            runOnWorkerThread(r);
+                                        }
+                                    }
+                                });
                 }
             }
             // Huh? Shouldn't this be inside the Runnable below?
@@ -1741,7 +1696,7 @@
             mBgAllAppsList.added = new ArrayList<AppInfo>();
 
             // Post callback on main thread
-            mHandler.post(new Runnable() {
+            mUiExecutor.execute(new Runnable() {
                 public void run() {
 
                     final long bindTime = SystemClock.uptimeMillis();
@@ -1765,11 +1720,8 @@
             }
         }
 
-        private void loadAndBindDeepShortcuts() {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
-            }
-            if (!mDeepShortcutsLoaded) {
+        private void loadDeepShortcuts() {
+            if (!mModelLoaded) {
                 sBgDataModel.deepShortcutMap.clear();
                 DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
                 mHasShortcutHostPermission = shortcutManager.hasHostPermission();
@@ -1782,14 +1734,7 @@
                         }
                     }
                 }
-                synchronized (LoaderTask.this) {
-                    if (mStopped) {
-                        return;
-                    }
-                    mDeepShortcutsLoaded = true;
-                }
             }
-            bindDeepShortcuts();
         }
     }
 
@@ -1805,7 +1750,7 @@
                 }
             }
         };
-        runOnMainThread(r);
+        mUiExecutor.execute(r);
     }
 
     /**
@@ -1831,6 +1776,12 @@
     }
 
     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);
         runOnWorkerThread(task);
     }
@@ -1850,12 +1801,12 @@
     public static abstract class BaseModelUpdateTask implements Runnable {
 
         private LauncherModel mModel;
-        private DeferredHandler mUiHandler;
+        private Executor mUiExecutor;
 
         /* package private */
         void init(LauncherModel model) {
             mModel = model;
-            mUiHandler = mModel.mHandler;
+            mUiExecutor = mModel.mUiExecutor;
         }
 
         @Override
@@ -1878,7 +1829,7 @@
          */
         public final void scheduleCallbackTask(final CallbackTask task) {
             final Callbacks callbacks = mModel.getCallback();
-            mUiHandler.post(new Runnable() {
+            mUiExecutor.execute(new Runnable() {
                 public void run() {
                     Callbacks cb = mModel.getCallback();
                     if (callbacks == cb && cb != null) {
@@ -1923,7 +1874,7 @@
     private void bindWidgetsModel(final Callbacks callbacks) {
         final MultiHashMap<PackageItemInfo, WidgetItem> widgets
                 = mBgWidgetsModel.getWidgetsMap().clone();
-        mHandler.post(new Runnable() {
+        mUiExecutor.execute(new Runnable() {
             @Override
             public void run() {
                 Callbacks cb = getCallback();
@@ -1980,14 +1931,6 @@
         }
     }
 
-    @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.
      */
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e71ef2c..b83ddb9 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -53,7 +53,6 @@
 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.logging.FileLog;
 import com.android.launcher3.provider.LauncherDbUtils;
@@ -63,6 +62,8 @@
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Thunk;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -74,7 +75,7 @@
 
     private static final int DATABASE_VERSION = 27;
 
-    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";
 
@@ -85,9 +86,21 @@
 
     protected DatabaseHelper mOpenHelper;
 
+    /**
+     * $ adb shell dumpsys activity provider com.android.launcher3
+     */
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+        if (appState == null || !appState.getModel().isModelLoaded()) {
+            return;
+        }
+        appState.getModel().dumpState("", fd, writer, args);
+    }
+
     @Override
     public boolean onCreate() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD) {
             Log.d(TAG, "Launcher process started");
         }
         mListenerHandler = new Handler(mListenerWrapper);
@@ -174,7 +187,7 @@
         if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
             LauncherAppState app = LauncherAppState.getInstanceNoCreate();
             if (app != null) {
-                app.reloadWorkspace();
+                app.getModel().forceReload();
             }
         }
     }
@@ -205,7 +218,7 @@
             // Deprecated behavior to support legacy devices which rely on provider callbacks.
             LauncherAppState app = LauncherAppState.getInstanceNoCreate();
             if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
-                app.reloadWorkspace();
+                app.getModel().forceReload();
             }
 
             String notify = uri.getQueryParameter("notify");
@@ -406,18 +419,13 @@
                 return result;
             }
             case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
-                createEmptyDB();
+                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                 return null;
             }
             case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
                 loadDefaultFavoritesIfNecessary();
                 return null;
             }
-            case LauncherSettings.Settings.METHOD_DELETE_DB: {
-                // Are you sure? (y/n)
-                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
-                return null;
-            }
         }
         return null;
     }
@@ -469,13 +477,6 @@
         values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
     }
 
-    /**
-     * Clears all the data for a fresh start.
-     */
-    synchronized private void createEmptyDB() {
-        mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
-    }
-
     private void clearFlagEmptyDbCreated() {
         Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
     }
@@ -518,12 +519,12 @@
 
             // There might be some partially restored DB items, due to buggy restore logic in
             // previous versions of launcher.
-            createEmptyDB();
+            mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
             // Populate favorites table with initial favorites
             if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
                     && usingExternallyProvidedLayout) {
                 // Unable to load external layout. Cleanup and load the internal layout.
-                createEmptyDB();
+                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
                         getDefaultLayoutParser(widgetHost));
             }
@@ -825,9 +826,15 @@
          * Clears all the data for a fresh start.
          */
         public void createEmptyDB(SQLiteDatabase db) {
-            db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
-            db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
-            onCreate(db);
+            db.beginTransaction();
+            try {
+                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();
+            }
         }
 
         /**
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index e450785..e8e0eb2 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);
         }
 
@@ -280,7 +278,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";
@@ -291,7 +289,6 @@
         public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id";
 
         public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
-        public static final String METHOD_DELETE_DB = "delete_db";
 
         public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
 
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 39c466d..f5af979 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -32,7 +32,7 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimationLayerSet;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
 
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/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
new file mode 100644
index 0000000..e8bf0a5
--- /dev/null
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.launcher3.compat.LauncherAppsCompat;
+
+import java.util.List;
+
+/**
+ * BroadcastReceiver to handle session commit intent.
+ */
+public class SessionCommitReceiver extends BroadcastReceiver {
+
+    // Preference key for automatically adding icon to homescreen.
+    public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!isEnabled(context)) {
+            // User has decided to not add icons on homescreen.
+            return;
+        }
+
+        SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
+        UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+        // TODO: Verify install reason
+        if (TextUtils.isEmpty(info.getAppPackageName())) {
+            return;
+        }
+
+        if (!Process.myUserHandle().equals(user)) {
+            // Managed profile is handled using ManagedProfileHeuristic
+            return;
+        }
+
+        List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
+                .getActivityList(info.getAppPackageName(), user);
+        if (activities == null || activities.isEmpty()) {
+            // no activity found
+            return;
+        }
+        InstallShortcutReceiver.queueActivityInfo(activities.get(0), context);
+    }
+
+    public static boolean isEnabled(Context context) {
+        return Utilities.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
+    }
+}
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index cedeb39..552e24a 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -25,6 +25,7 @@
 import android.preference.PreferenceFragment;
 import android.provider.Settings;
 import android.provider.Settings.System;
+import android.support.v4.os.BuildCompat;
 
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -72,6 +73,11 @@
                 mRotationLockObserver.onChange(true);
                 rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
             }
+
+            if (!BuildCompat.isAtLeastO()) {
+                getPreferenceScreen().removePreference(
+                        findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
+            }
         }
 
         @Override
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index b35dcb7..f0bb1c0 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -134,7 +134,6 @@
         title = info.title;
         intent = new Intent(info.intent);
         iconResource = info.iconResource;
-        iconBitmap = info.iconBitmap;
         status = info.status;
         mInstallProgress = info.mInstallProgress;
         isDisabled = info.isDisabled;
@@ -146,8 +145,6 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         isDisabled = info.isDisabled;
-        iconBitmap = info.iconBitmap;
-        usingLowResIcon = info.usingLowResIcon;
     }
 
     /**
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index e68a5b0..0fac29f 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -5,6 +5,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -139,9 +140,10 @@
             final Runnable checkIfUninstallWasSuccess = new Runnable() {
                 @Override
                 public void run() {
-                    String packageName = cn.getPackageName();
-                    boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
-                            launcher, packageName, user);
+                    // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
+                    boolean uninstallSuccessful = LauncherAppsCompat.getInstance(launcher)
+                            .getApplicationInfo(cn.getPackageName(),
+                                    PackageManager.MATCH_UNINSTALLED_PACKAGES, user) == null;
                     callback.onDragObjectRemoved(uninstallSuccessful);
                 }
             };
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index abc5367..2413d8a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -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;
@@ -575,7 +575,7 @@
             try {
                 c.close();
             } catch (IOException e) {
-                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_DOGFOOD_BUILD) {
                     Log.d(TAG, "Error closing", e);
                 }
             }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6d52ea3..8f8d32c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -65,7 +65,6 @@
 import com.android.launcher3.badge.FolderBadgeInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -642,7 +641,8 @@
         // 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.
-        if (getChildCount() > 0) {
+        // 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();
 
@@ -2617,7 +2617,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],
@@ -2950,7 +2950,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;
@@ -3607,7 +3607,7 @@
                     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. ");
             };
@@ -3633,7 +3633,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
@@ -3884,7 +3884,9 @@
                     // The item may belong to a folder.
                     View parent = idToViewMap.get(itemToRemove.container);
                     if (parent != null) {
-                        ((FolderInfo) parent.getTag()).remove((ShortcutInfo) itemToRemove, false);
+                        FolderInfo folderInfo = (FolderInfo) parent.getTag();
+                        folderInfo.prepareAutoUpdate();
+                        folderInfo.remove((ShortcutInfo) itemToRemove, false);
                     }
                 }
             }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 0732004..cc5fa8c 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,6 +20,8 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.InsetDrawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
 import android.text.Spannable;
@@ -46,6 +48,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 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;
@@ -211,7 +215,7 @@
 
         // IF scroller is at the very top OR there is no scroll bar because there is probably not
         // enough items to scroll, THEN it's okay for the container to be pulled down.
-        if (mAppsRecyclerView.getScrollBar().getThumbOffsetY() <= 0) {
+        if (mAppsRecyclerView.getCurrentScrollY() == 0) {
             return true;
         }
         return false;
@@ -425,14 +429,22 @@
     @Override
     public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
         if (apps != null) {
-            if (mApps.setOrderedFilter(apps)) {
-                mAppsRecyclerView.onSearchResultsChanged();
-            }
+            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();
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index 28b7685..a1ff822 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -19,6 +19,7 @@
 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;
 
@@ -45,8 +46,7 @@
 
     // Set of all views animated during fast scroll.  We keep track of these ourselves since there
     // is no way to reset a view once it gets scrapped or recycled without other hacks
-    private HashSet<BaseRecyclerViewFastScrollBar.FastScrollFocusableView> mTrackedFastScrollViews =
-            new HashSet<>();
+    private HashSet<RecyclerView.ViewHolder> mTrackedFastScrollViews = new HashSet<>();
 
     // Smooth fast-scroll animation frames
     @Thunk int mFastScrollFrameIndex;
@@ -186,12 +186,7 @@
     public void onBindView(AllAppsGridAdapter.ViewHolder holder) {
         // Update newly bound views to the current fast scroll state if we are fast scrolling
         if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) {
-            if (holder.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
-                BaseRecyclerViewFastScrollBar.FastScrollFocusableView v =
-                        (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) holder.itemView;
-                updateViewFastScrollFocusState(v, holder.getPosition(), false /* animated */);
-                mTrackedFastScrollViews.add(v);
-            }
+            mTrackedFastScrollViews.add(holder);
         }
     }
 
@@ -201,9 +196,9 @@
     private void trackAllChildViews() {
         int childCount = mRv.getChildCount();
         for (int i = 0; i < childCount; i++) {
-            View v = mRv.getChildAt(i);
-            if (v instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
-                mTrackedFastScrollViews.add((BaseRecyclerViewFastScrollBar.FastScrollFocusableView) v);
+            RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder(mRv.getChildAt(i));
+            if (viewHolder != null) {
+                mTrackedFastScrollViews.add(viewHolder);
             }
         }
     }
@@ -212,27 +207,16 @@
      * Updates the fast scroll focus on all the children.
      */
     private void updateTrackedViewsFastScrollFocusState() {
-        for (BaseRecyclerViewFastScrollBar.FastScrollFocusableView v : mTrackedFastScrollViews) {
-            RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder((View) v);
-            int pos = (viewHolder != null) ? viewHolder.getPosition() : -1;
-            updateViewFastScrollFocusState(v, pos, true);
+        for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
+            int pos = viewHolder.getAdapterPosition();
+            boolean isActive = false;
+            if (mCurrentFastScrollSection != null && pos > -1) {
+                AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
+                isActive = item != null &&
+                        mCurrentFastScrollSection.equals(item.sectionName) &&
+                        item.position == mTargetFastScrollPosition;
+            }
+            viewHolder.itemView.setActivated(isActive);
         }
     }
-
-    /**
-     * Updates the fast scroll focus on all a given view.
-     */
-    private void updateViewFastScrollFocusState(BaseRecyclerViewFastScrollBar.FastScrollFocusableView v,
-                                                int pos, boolean animated) {
-        FastBitmapDrawable.State newState = FastBitmapDrawable.State.NORMAL;
-        if (mCurrentFastScrollSection != null && pos > -1) {
-            AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
-            boolean highlight = item.sectionName.equals(mCurrentFastScrollSection) &&
-                    item.position == mTargetFastScrollPosition;
-            newState = highlight ?
-                    FastBitmapDrawable.State.FAST_SCROLL_HIGHLIGHTED :
-                    FastBitmapDrawable.State.FAST_SCROLL_UNHIGHLIGHTED;
-        }
-        v.setFastScrollFocusState(newState, animated);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index bd877f2..59cac8d 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -19,8 +19,8 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -33,11 +33,15 @@
 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.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
+import com.android.launcher3.discovery.AppDiscoveryItemView;
+
+import java.util.List;
 
 /**
  * The grid view adapter of all the apps.
@@ -64,6 +68,8 @@
     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;
 
     // Common view type masks
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER
@@ -71,6 +77,8 @@
             | 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 interface BindViewCallback {
@@ -81,7 +89,6 @@
      * ViewHolder for each icon.
      */
     public static class ViewHolder extends RecyclerView.ViewHolder {
-
         public ViewHolder(View v) {
             super(v);
         }
@@ -105,17 +112,53 @@
             final AccessibilityRecordCompat record = AccessibilityEventCompat
                     .asRecord(event);
             record.setItemCount(mApps.getNumFilteredApps());
+            record.setFromIndex(Math.max(0,
+                    record.getFromIndex() - getRowsNotForAccessibility(record.getFromIndex())));
+            record.setToIndex(Math.max(0,
+                    record.getToIndex() - getRowsNotForAccessibility(record.getToIndex())));
         }
 
         @Override
         public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
                 RecyclerView.State state) {
-            if (mApps.hasNoFilteredResults()) {
-                // Disregard the no-search-results text as a list item for accessibility
-                return 0;
-            } else {
-                return super.getRowCountForAccessibility(recycler, state);
+            return super.getRowCountForAccessibility(recycler, state) -
+                    getRowsNotForAccessibility(mApps.getAdapterItems().size() - 1);
+        }
+
+        @Override
+        public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
+                RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
+            super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
+
+            ViewGroup.LayoutParams lp = host.getLayoutParams();
+            AccessibilityNodeInfoCompat.CollectionItemInfoCompat cic = info.getCollectionItemInfo();
+            if (!(lp instanceof LayoutParams) || (cic == null)) {
+                return;
             }
+            LayoutParams glp = (LayoutParams) lp;
+            info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
+                    cic.getRowIndex() - getRowsNotForAccessibility(glp.getViewAdapterPosition()),
+                    cic.getRowSpan(),
+                    cic.getColumnIndex(),
+                    cic.getColumnSpan(),
+                    cic.isHeading(),
+                    cic.isSelected()));
+        }
+
+        /**
+         * Returns the number of rows before {@param adapterPosition}, including this position
+         * which should not be counted towards the collection info.
+         */
+        private int getRowsNotForAccessibility(int adapterPosition) {
+            List<AdapterItem> items = mApps.getAdapterItems();
+            adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
+            int extraRows = 0;
+            for (int i = 0; i <= adapterPosition; i++) {
+                if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_CONTENT)) {
+                    extraRows++;
+                }
+            }
+            return extraRows;
         }
 
         @Override
@@ -234,8 +277,7 @@
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case VIEW_TYPE_ICON:
-                /* falls through */
-            case VIEW_TYPE_PREDICTION_ICON: {
+            case VIEW_TYPE_PREDICTION_ICON:
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.all_apps_icon, parent, false);
                 icon.setOnClickListener(mIconClickListener);
@@ -245,14 +287,14 @@
                 icon.setOnFocusChangeListener(mIconFocusListener);
 
                 // Ensure the all apps icon height matches the workspace icons
-                DeviceProfile profile = mLauncher.getDeviceProfile();
-                Point cellSize = profile.getCellSize();
-                GridLayoutManager.LayoutParams lp =
-                        (GridLayoutManager.LayoutParams) icon.getLayoutParams();
-                lp.height = cellSize.y;
-                icon.setLayoutParams(lp);
+                icon.getLayoutParams().height = getCellSize().y;
                 return new ViewHolder(icon);
-            }
+            case VIEW_TYPE_DISCOVERY_ITEM:
+                AppDiscoveryItemView appDiscoveryItemView = (AppDiscoveryItemView) mLayoutInflater
+                        .inflate(R.layout.all_apps_discovery_item, parent, false);
+                appDiscoveryItemView.init(mIconClickListener, mLauncher.getAccessibilityDelegate(),
+                        mIconLongClickListener);
+                return new ViewHolder(appDiscoveryItemView);
             case VIEW_TYPE_EMPTY_SEARCH:
                 return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
                         parent, false));
@@ -269,8 +311,11 @@
             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);
+                return new ViewHolder(loadingDividerView);
             case VIEW_TYPE_PREDICTION_DIVIDER:
-                /* falls through */
             case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.all_apps_divider, parent, false));
@@ -279,23 +324,26 @@
         }
     }
 
+    private Point getCellSize() {
+        return mLauncher.getDeviceProfile().getCellSize();
+    }
+
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
-            case VIEW_TYPE_ICON: {
+            case VIEW_TYPE_ICON:
+            case VIEW_TYPE_PREDICTION_ICON:
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.applyFromApplicationInfo(info);
                 icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
-            }
-            case VIEW_TYPE_PREDICTION_ICON: {
-                AppInfo info = mApps.getAdapterItems().get(position).appInfo;
-                BubbleTextView icon = (BubbleTextView) holder.itemView;
-                icon.applyFromApplicationInfo(info);
-                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+            case VIEW_TYPE_DISCOVERY_ITEM:
+                AppDiscoveryAppInfo appDiscoveryAppInfo = (AppDiscoveryAppInfo)
+                        mApps.getAdapterItems().get(position).appInfo;
+                AppDiscoveryItemView view = (AppDiscoveryItemView) holder.itemView;
+                view.apply(appDiscoveryAppInfo);
                 break;
-            }
             case VIEW_TYPE_EMPTY_SEARCH:
                 TextView emptyViewText = (TextView) holder.itemView;
                 emptyViewText.setText(mEmptySearchMessage);
@@ -310,6 +358,15 @@
                     searchView.setVisibility(View.GONE);
                 }
                 break;
+            case VIEW_TYPE_APPS_LOADING_DIVIDER:
+                int visLoading = mApps.isAppDiscoveryRunning() ? View.VISIBLE : View.GONE;
+                int visLoaded = !mApps.isAppDiscoveryRunning() ? View.VISIBLE : View.GONE;
+                holder.itemView.findViewById(R.id.loadingProgressBar).setVisibility(visLoading);
+                holder.itemView.findViewById(R.id.loadedDivider).setVisibility(visLoaded);
+                break;
+            case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
+                // nothing to do
+                break;
         }
         if (mBindViewCallback != null) {
             mBindViewCallback.onBindView(holder);
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a41d832..64e2fcb 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -30,6 +30,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
@@ -110,44 +111,40 @@
      * all the different view types.
      */
     public void preMeasureViews(AllAppsGridAdapter adapter) {
+        View icon = adapter.onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON).itemView;
+        final int iconHeight = icon.getLayoutParams().height;
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight);
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight);
+
         final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
                 getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST);
         final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
                 getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST);
 
-        // Icons
-        BubbleTextView icon = (BubbleTextView) adapter.onCreateViewHolder(this,
-                AllAppsGridAdapter.VIEW_TYPE_ICON).itemView;
-        int iconHeight = icon.getLayoutParams().height;
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight);
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight);
+        putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
+                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);
 
-        // Search divider
-        View searchDivider = adapter.onCreateViewHolder(this,
-                AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER).itemView;
-        searchDivider.measure(widthMeasureSpec, heightMeasureSpec);
-        int searchDividerHeight = searchDivider.getMeasuredHeight();
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, searchDividerHeight);
+        if (FeatureFlags.DISCOVERY_ENABLED) {
+            putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
+                    AllAppsGridAdapter.VIEW_TYPE_APPS_LOADING_DIVIDER);
+            putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
+                    AllAppsGridAdapter.VIEW_TYPE_DISCOVERY_ITEM);
+        }
+    }
 
-        // Generic dividers
-        View divider = adapter.onCreateViewHolder(this,
-                AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER).itemView;
-        divider.measure(widthMeasureSpec, heightMeasureSpec);
-        int dividerHeight = divider.getMeasuredHeight();
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, dividerHeight);
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, dividerHeight);
-
-        // Search views
-        View emptySearch = adapter.onCreateViewHolder(this,
-                AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH).itemView;
-        emptySearch.measure(widthMeasureSpec, heightMeasureSpec);
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH,
-                emptySearch.getMeasuredHeight());
-        View searchMarket = adapter.onCreateViewHolder(this,
-                AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET).itemView;
-        searchMarket.measure(widthMeasureSpec, heightMeasureSpec);
-        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET,
-                searchMarket.getMeasuredHeight());
+    private void putSameHeightFor(AllAppsGridAdapter adapter, int w, int h, int... viewTypes) {
+        View view = adapter.onCreateViewHolder(this, viewTypes[0]).itemView;
+        view.measure(w, h);
+        for (int viewType : viewTypes) {
+            mViewHeights.put(viewType, view.getMeasuredHeight());
+        }
     }
 
     /**
@@ -207,7 +204,7 @@
         // Always scroll the view to the top so the user can see the changed results
         scrollToTop();
 
-        if (mApps.hasNoFilteredResults()) {
+        if (mApps.shouldShowEmptySearch()) {
             if (mEmptySearchBackground == null) {
                 mEmptySearchBackground = DrawableFactory.get(getContext())
                         .getAllAppsBackground(getContext());
@@ -438,4 +435,5 @@
                 x + mEmptySearchBackground.getIntrinsicWidth(),
                 y + mEmptySearchBackground.getIntrinsicHeight());
     }
+
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 365ab31..c7ba3ab 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -19,6 +19,8 @@
 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;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -32,6 +34,8 @@
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.discovery.AppDiscoveryItem;
+import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.util.ComponentKey;
 
 import java.util.ArrayList;
@@ -46,7 +50,7 @@
     protected AlphabeticalAppsList mApps;
     protected Callbacks mCb;
     protected ExtendedEditText mInput;
-    private String mQuery;
+    protected String mQuery;
 
     protected DefaultAppSearchAlgorithm mSearchAlgorithm;
     protected InputMethodManager mInputMethodManager;
@@ -73,6 +77,14 @@
                 mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
         mSearchAlgorithm = onInitializeSearch();
+
+        onInitialized();
+    }
+
+    /**
+     * You can override this method to perform custom initialization.
+     */
+    protected void onInitialized() {
     }
 
     /**
@@ -117,6 +129,7 @@
         if (actionId != EditorInfo.IME_ACTION_SEARCH) {
             return false;
         }
+
         // Skip if the query is empty
         String query = v.getText().toString();
         if (query.isEmpty()) {
@@ -206,5 +219,19 @@
          * Called when the search results should be cleared.
          */
         void clearSearchResult();
+
+
+        /**
+         * Called when the app discovery is providing an update of search, which can either be
+         * START for starting a new discovery,
+         * UPDATE for providing a new search result, can be called multiple times,
+         * END for indicating the end of results.
+         *
+         * @param app result item if UPDATE, else null
+         * @param app the update state, START, UPDATE or END
+         */
+        void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
+                @NonNull AppDiscoveryUpdateState state);
     }
+
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 0bfbd3e..9e32d25 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -17,12 +17,17 @@
 
 import android.content.Context;
 import android.os.Process;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.util.Log;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryAppInfo;
+import com.android.launcher3.discovery.AppDiscoveryItem;
+import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LabelComparator;
 
@@ -48,6 +53,8 @@
 
     private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
 
+    private AppDiscoveryUpdateState mAppDiscoveryUpdateState;
+
     /**
      * Info about a fast scroller section, depending if sections are merged, the fast scroller
      * sections will not be the same set as the section headers.
@@ -106,6 +113,17 @@
             return item;
         }
 
+        public static AdapterItem asDiscoveryItem(int pos, String sectionName, AppInfo appInfo,
+                int appIndex) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_DISCOVERY_ITEM;
+            item.position = pos;
+            item.sectionName = sectionName;
+            item.appInfo = appInfo;
+            item.appIndex = appIndex;
+            return item;
+        }
+
         public static AdapterItem asEmptySearch(int pos) {
             AdapterItem item = new AdapterItem();
             item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
@@ -134,6 +152,13 @@
             return item;
         }
 
+        public static AdapterItem asLoadingDivider(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_APPS_LOADING_DIVIDER;
+            item.position = pos;
+            return item;
+        }
+
         public static AdapterItem asMarketSearch(int pos) {
             AdapterItem item = new AdapterItem();
             item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
@@ -142,22 +167,24 @@
         }
     }
 
-    private Launcher mLauncher;
+    private final Launcher mLauncher;
 
     // The set of apps from the system not including predictions
     private final List<AppInfo> mApps = new ArrayList<>();
     private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
 
     // The set of filtered apps with the current filter
-    private List<AppInfo> mFilteredApps = new ArrayList<>();
+    private final List<AppInfo> mFilteredApps = new ArrayList<>();
     // The current set of adapter items
-    private List<AdapterItem> mAdapterItems = new ArrayList<>();
+    private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
     // The set of sections that we allow fast-scrolling to (includes non-merged sections)
-    private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
+    private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
     // The set of predicted app component names
-    private List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
+    private final List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
     // The set of predicted apps resolved from the component names and the current set of apps
-    private List<AppInfo> mPredictedApps = new ArrayList<>();
+    private final List<AppInfo> mPredictedApps = new ArrayList<>();
+    private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
+
     // The of ordered component names as a result of a search query
     private ArrayList<ComponentKey> mSearchResults;
     private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
@@ -240,6 +267,10 @@
         return (mSearchResults != null) && mFilteredApps.isEmpty();
     }
 
+    boolean shouldShowEmptySearch() {
+        return hasNoFilteredResults() && !isAppDiscoveryRunning() && mDiscoveredApps.isEmpty();
+    }
+
     /**
      * Sets the sorted list of filtered components.
      */
@@ -253,6 +284,20 @@
         return false;
     }
 
+    public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
+                @NonNull AppDiscoveryUpdateState state) {
+        mAppDiscoveryUpdateState = state;
+        switch (state) {
+            case START:
+                mDiscoveredApps.clear();
+                break;
+            case UPDATE:
+                mDiscoveredApps.add(new AppDiscoveryAppInfo(app, mLauncher));
+                break;
+        }
+        updateAdapterItems();
+    }
+
     /**
      * Sets the current set of predicted apps.  Since this can be called before we get the full set
      * of applications, we should merge the results only in onAppsUpdated() which is idempotent.
@@ -350,6 +395,17 @@
      * mCachedSectionNames to have been calculated for the set of all apps in mApps.
      */
     private void updateAdapterItems() {
+        refillAdapterItems();
+        refreshRecyclerView();
+    }
+
+    private void refreshRecyclerView() {
+        if (mAdapter != null) {
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private void refillAdapterItems() {
         String lastSectionName = null;
         FastScrollSectionInfo lastFastScrollerSectionInfo = null;
         int position = 0;
@@ -384,7 +440,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);
                     }
                 }
@@ -435,14 +491,30 @@
             mFilteredApps.add(info);
         }
 
-        // Append the search market item if we are currently searching
         if (hasFilter()) {
-            if (hasNoFilteredResults()) {
-                mAdapterItems.add(AdapterItem.asEmptySearch(position++));
+            if (isAppDiscoveryRunning() || mDiscoveredApps.size() > 0) {
+                mAdapterItems.add(AdapterItem.asLoadingDivider(position++));
+
+                // Append all app discovery results
+                for (int i = 0; i < mDiscoveredApps.size(); i++) {
+                    AppDiscoveryAppInfo appDiscoveryAppInfo = mDiscoveredApps.get(i);
+                    AdapterItem item = AdapterItem.asDiscoveryItem(position++,
+                            "", appDiscoveryAppInfo, appIndex++);
+                    mAdapterItems.add(item);
+                }
+
+                if (!isAppDiscoveryRunning()) {
+                    mAdapterItems.add(AdapterItem.asMarketSearch(position++));
+                }
             } else {
-                mAdapterItems.add(AdapterItem.asMarketDivider(position++));
+                // Append the search market item
+                if (hasNoFilteredResults()) {
+                    mAdapterItems.add(AdapterItem.asEmptySearch(position++));
+                } else {
+                    mAdapterItems.add(AdapterItem.asMarketDivider(position++));
+                }
+                mAdapterItems.add(AdapterItem.asMarketSearch(position++));
             }
-            mAdapterItems.add(AdapterItem.asMarketSearch(position++));
         }
 
         if (mNumAppsPerRow != 0) {
@@ -498,11 +570,11 @@
                     break;
             }
         }
+    }
 
-        // Refresh the recycler view
-        if (mAdapter != null) {
-            mAdapter.notifyDataSetChanged();
-        }
+    public boolean isAppDiscoveryRunning() {
+        return mAppDiscoveryUpdateState == AppDiscoveryUpdateState.START
+                || mAppDiscoveryUpdateState == AppDiscoveryUpdateState.UPDATE;
     }
 
     private List<AppInfo> getFiltersAppInfos() {
@@ -532,4 +604,5 @@
         }
         return sectionName;
     }
+
 }
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
new file mode 100644
index 0000000..679e8e3
--- /dev/null
+++ b/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * 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/PillRevealOutlineProvider.java b/src/com/android/launcher3/anim/PillRevealOutlineProvider.java
similarity index 98%
rename from src/com/android/launcher3/util/PillRevealOutlineProvider.java
rename to src/com/android/launcher3/anim/PillRevealOutlineProvider.java
index a57d69f..450f9db 100644
--- a/src/com/android/launcher3/util/PillRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/PillRevealOutlineProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
 
 import android.graphics.Rect;
 import android.view.ViewOutlineProvider;
diff --git a/src/com/android/launcher3/anim/PropertyResetListener.java b/src/com/android/launcher3/anim/PropertyResetListener.java
new file mode 100644
index 0000000..eefb014
--- /dev/null
+++ b/src/com/android/launcher3/anim/PropertyResetListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.util.Property;
+
+/**
+ * An AnimatorListener that sets the given property to the given value at the end of the animation.
+ */
+public class PropertyResetListener<T, V> extends AnimatorListenerAdapter {
+
+    private Property<T, V> mPropertyToReset;
+    private V mResetToValue;
+
+    public PropertyResetListener(Property<T, V> propertyToReset, V resetToValue) {
+        mPropertyToReset = propertyToReset;
+        mResetToValue = resetToValue;
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        mPropertyToReset.set((T) ((ObjectAnimator) animation).getTarget(), mResetToValue);
+    }
+}
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..9c09477
--- /dev/null
+++ b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * 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;
+
+    public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+            Rect endRect) {
+        mStartRadius = startRadius;
+        mEndRadius = endRadius;
+        mStartRect = startRect;
+        mEndRect = endRect;
+    }
+
+    @Override
+    public boolean shouldRemoveElevationDuringAnimation() {
+        return false;
+    }
+
+    @Override
+    public void setProgress(float progress) {
+        mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
+
+        mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
+        mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
+        mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
+        mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
+    }
+}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 8bbc2af..5896928 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -20,14 +20,15 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.Shader;
 import android.support.annotation.Nullable;
 
 import com.android.launcher3.R;
 import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.ShadowGenerator;
 
 /**
  * Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
@@ -40,10 +41,12 @@
     private final int mTextHeight;
     private final IconDrawer mLargeIconDrawer;
     private final IconDrawer mSmallIconDrawer;
-    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
+            | Paint.FILTER_BITMAP_FLAG);
+    private final Bitmap mBackgroundWithShadow;
 
-    public BadgeRenderer(Context context) {
+    public BadgeRenderer(final Context context) {
         mContext = context;
         Resources res = context.getResources();
         mSize = res.getDimensionPixelSize(R.dimen.badge_size);
@@ -51,10 +54,13 @@
         mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
         mTextPaint.setTextAlign(Paint.Align.CENTER);
         mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.badge_text_size));
+        mTextPaint.setFakeBoldText(true);
         // Measure the text height.
         Rect tempTextHeight = new Rect();
         mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
         mTextHeight = tempTextHeight.height();
+
+        mBackgroundWithShadow = ShadowGenerator.createCircleWithShadow(Color.WHITE, mSize);
     }
 
     /**
@@ -67,13 +73,15 @@
      */
     public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
             Rect iconBounds, float badgeScale) {
-        mBackgroundPaint.setColor(palette.backgroundColor);
         mTextPaint.setColor(palette.textColor);
         canvas.save(Canvas.MATRIX_SAVE_FLAG);
         // We draw the badge relative to its center.
         canvas.translate(iconBounds.right - mSize / 2, iconBounds.top + mSize / 2);
         canvas.scale(badgeScale, badgeScale);
-        canvas.drawCircle(0, 0, mSize / 2, mBackgroundPaint);
+        mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
+        int backgroundSize = mBackgroundWithShadow.getHeight(); // Same as width.
+        canvas.drawBitmap(mBackgroundWithShadow, -backgroundSize / 2, -backgroundSize / 2,
+                mBackgroundPaint);
         IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
                 ? mLargeIconDrawer : mSmallIconDrawer;
         Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index b9142ed..2eb5b02 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -72,6 +73,8 @@
             UserHandle user);
     public abstract void startActivityForProfile(ComponentName component, UserHandle user,
             Rect sourceBounds, Bundle opts);
+    public abstract ApplicationInfo getApplicationInfo(
+            String packageName, int flags, UserHandle user);
     public abstract void showAppDetailsForProfile(ComponentName component, UserHandle user);
     public abstract void addOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
     public abstract void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 3cb721c..4590173 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
@@ -26,6 +27,7 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.UserHandle;
 
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
@@ -65,6 +67,32 @@
     }
 
     @Override
+    public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
+        final boolean isPrimaryUser = Process.myUserHandle().equals(user);
+        if (!isPrimaryUser && (flags == 0)) {
+            // We are looking for an installed app on a secondary profile. Prior to O, the only
+            // entry point for work profiles is through the LauncherActivity.
+            List<LauncherActivityInfo> activityList =
+                    mLauncherApps.getActivityList(packageName, user);
+            return activityList.size() > 0 ? activityList.get(0).getApplicationInfo() : null;
+        }
+        try {
+            ApplicationInfo info =
+                    mContext.getPackageManager().getApplicationInfo(packageName, flags);
+            // There is no way to check if the app is installed for managed profile. But for
+            // primary profile, we can still have this check.
+            if (isPrimaryUser && ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0)
+                    || !info.enabled) {
+                return null;
+            }
+            return info;
+        } catch (PackageManager.NameNotFoundException e) {
+            // Package not found
+            return null;
+        }
+    }
+
+    @Override
     public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
         mLauncherApps.startAppDetailsActivity(component, user, null, null);
     }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 0610726..2743379 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.os.UserHandle;
@@ -34,6 +35,13 @@
     }
 
     @Override
+    public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
+        ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
+        return info == null || (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
+                ? null : info;
+    }
+
+    @Override
     public List<ShortcutConfigActivityInfo> getCustomShortcutActivityList() {
         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
 
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
new file mode 100644
index 0000000..50e979a
--- /dev/null
+++ b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.discovery;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+
+public class AppDiscoveryAppInfo extends AppInfo {
+
+    private final @NonNull Launcher mLauncher;
+
+    public final boolean showAsDiscoveryItem;
+    public final boolean isInstantApp;
+    public final float rating;
+    public final long reviewCount;
+    public final @NonNull String publisher;
+    public final @NonNull Intent installIntent;
+    public final @NonNull Intent launchIntent;
+    public final @Nullable String priceFormatted;
+
+    public AppDiscoveryAppInfo(AppDiscoveryItem item, Launcher launcher) {
+        this.mLauncher = launcher;
+        this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
+        this.title = item.title;
+        this.iconBitmap = item.bitmap;
+        this.isDisabled = ShortcutInfo.DEFAULT;
+        this.usingLowResIcon = false;
+        this.isInstantApp = item.isInstantApp;
+        this.rating = item.starRating;
+        this.showAsDiscoveryItem = true;
+        this.publisher = item.publisher != null ? item.publisher : "";
+        this.priceFormatted = item.price;
+        this.componentName = new ComponentName(item.packageName, "");
+        this.installIntent = item.installIntent;
+        this.launchIntent = item.launchIntent;
+        this.reviewCount = item.reviewCount;
+        this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+    }
+
+    @Override
+    public ShortcutInfo makeShortcut() {
+        if (!isDragAndDropSupported()) {
+            throw new RuntimeException("DnD is currently not supported for discovered store apps");
+        }
+        ShortcutInfo shortcutInfo = super.makeShortcut();
+        if (isInstantApp) {
+            int iconSize = iconBitmap.getWidth();
+            int badgeSize = mLauncher.getResources().getDimensionPixelOffset(R.dimen.badge_size);
+            Bitmap icon = Bitmap.createBitmap(iconBitmap);
+            Drawable badgeDrawable = mLauncher.getDrawable(R.drawable.ic_instant_app);
+            badgeDrawable.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
+            Canvas canvas = new Canvas(icon);
+            badgeDrawable.draw(canvas);
+            shortcutInfo.iconBitmap = icon;
+        }
+        return shortcutInfo;
+    }
+
+    public boolean isDragAndDropSupported() {
+        return isInstantApp;
+    }
+
+}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItem.java b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
new file mode 100644
index 0000000..7c10371
--- /dev/null
+++ b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.discovery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+
+/**
+ * This class represents the model for a discovered app via app discovery.
+ * It holds all information for one result retrieved from an app discovery service.
+ */
+public class AppDiscoveryItem {
+
+    public final String packageName;
+    public final boolean isInstantApp;
+    public final float starRating;
+    public final long reviewCount;
+    public final Intent launchIntent;
+    public final Intent installIntent;
+    public final CharSequence title;
+    public final String publisher;
+    public final String price;
+    public final Bitmap bitmap;
+
+    public AppDiscoveryItem(String packageName,
+                            boolean isInstantApp,
+                            float starRating,
+                            long reviewCount,
+                            CharSequence title,
+                            String publisher,
+                            Bitmap bitmap,
+                            String price,
+                            Intent launchIntent,
+                            Intent installIntent) {
+        this.packageName = packageName;
+        this.isInstantApp = isInstantApp;
+        this.starRating = starRating;
+        this.reviewCount = reviewCount;
+        this.launchIntent = launchIntent;
+        this.installIntent = installIntent;
+        this.title = title;
+        this.publisher = publisher;
+        this.price = price;
+        this.bitmap = bitmap;
+    }
+
+}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItemView.java b/src/com/android/launcher3/discovery/AppDiscoveryItemView.java
new file mode 100644
index 0000000..6faad87
--- /dev/null
+++ b/src/com/android/launcher3/discovery/AppDiscoveryItemView.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.discovery;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+public class AppDiscoveryItemView extends RelativeLayout {
+
+    private static boolean SHOW_REVIEW_COUNT = false;
+
+    private ImageView mImage;
+    private ImageView mBadge;
+    private TextView mTitle;
+    private TextView mRatingText;
+    private RatingView mRatingView;
+    private TextView mReviewCount;
+    private TextView mPrice;
+    private OnLongClickListener mOnLongClickListener;
+
+    public AppDiscoveryItemView(Context context) {
+        this(context, null);
+    }
+
+    public AppDiscoveryItemView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppDiscoveryItemView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        this.mImage = (ImageView) findViewById(R.id.image);
+        this.mBadge = (ImageView) findViewById(R.id.badge);
+        this.mTitle = (TextView) findViewById(R.id.title);
+        this.mRatingText = (TextView) findViewById(R.id.rating);
+        this.mRatingView = (RatingView) findViewById(R.id.rating_view);
+        this.mPrice = (TextView) findViewById(R.id.price);
+        this.mReviewCount = (TextView) findViewById(R.id.review_count);
+    }
+
+    public void init(OnClickListener clickListener,
+                     AccessibilityDelegate accessibilityDelegate,
+                     OnLongClickListener onLongClickListener) {
+        setOnClickListener(clickListener);
+        mImage.setOnClickListener(clickListener);
+        setAccessibilityDelegate(accessibilityDelegate);
+        mOnLongClickListener = onLongClickListener;
+    }
+
+    public void apply(@NonNull AppDiscoveryAppInfo info) {
+        setTag(info);
+        mImage.setTag(info);
+        mImage.setImageBitmap(info.iconBitmap);
+        mImage.setOnLongClickListener(info.isDragAndDropSupported() ? mOnLongClickListener : null);
+        mBadge.setVisibility(info.isInstantApp ? View.VISIBLE : View.GONE);
+        mTitle.setText(info.title);
+        mPrice.setText(info.priceFormatted != null ? info.priceFormatted : "");
+        mReviewCount.setVisibility(SHOW_REVIEW_COUNT ? View.VISIBLE : View.GONE);
+        if (info.rating >= 0) {
+            mRatingText.setText(new DecimalFormat("#.#").format(info.rating));
+            mRatingView.setRating(info.rating);
+            mRatingView.setVisibility(View.VISIBLE);
+            String reviewCountFormatted = NumberFormat.getInstance().format(info.reviewCount);
+            mReviewCount.setText("(" + reviewCountFormatted + ")");
+        } else {
+            // if we don't have a rating
+            mRatingView.setVisibility(View.GONE);
+            mRatingText.setText("");
+            mReviewCount.setText("");
+        }
+    }
+}
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/discovery/AppDiscoveryUpdateState.java
similarity index 67%
rename from src_config/com/android/launcher3/config/ProviderConfig.java
rename to src/com/android/launcher3/discovery/AppDiscoveryUpdateState.java
index 491fa65..0700a10 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryUpdateState.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,8 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.config;
+package com.android.launcher3.discovery;
 
-public class ProviderConfig {
-
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
-    public static final boolean IS_DOGFOOD_BUILD = true;
+public enum AppDiscoveryUpdateState {
+    START, UPDATE, END
 }
diff --git a/src/com/android/launcher3/discovery/RatingView.java b/src/com/android/launcher3/discovery/RatingView.java
new file mode 100644
index 0000000..8fe63d6
--- /dev/null
+++ b/src/com/android/launcher3/discovery/RatingView.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.discovery;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.ClipDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+
+import com.android.launcher3.R;
+
+/**
+ * A simple rating view that shows stars with a rating from 0-5.
+ */
+public class RatingView extends View {
+
+    private static final float WIDTH_FACTOR = 0.9f;
+    private static final int MAX_LEVEL = 10000;
+    private static final int MAX_STARS = 5;
+
+    private final Drawable mStarDrawable;
+    private final int mColorGray;
+    private final int mColorHighlight;
+
+    private float rating;
+
+    public RatingView(Context context) {
+        this(context, null);
+    }
+
+    public RatingView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RatingView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mStarDrawable = getResources().getDrawable(R.drawable.ic_star_rating, null);
+        mColorGray = 0x1E000000;
+        mColorHighlight = 0x8A000000;
+    }
+
+    public void setRating(float rating) {
+        this.rating = Math.min(Math.max(rating, 0), MAX_STARS);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        drawStars(canvas, MAX_STARS, mColorGray);
+        drawStars(canvas, rating, mColorHighlight);
+    }
+
+    private void drawStars(Canvas canvas, float stars, int color) {
+        int fullWidth = getLayoutParams().width;
+        int cellWidth = fullWidth / MAX_STARS;
+        int starWidth = (int) (cellWidth * WIDTH_FACTOR);
+        int padding = cellWidth - starWidth;
+        int fullStars = (int) stars;
+        float partialStarFactor = stars - fullStars;
+
+        for (int i = 0; i < fullStars; i++) {
+            int x = i * cellWidth + padding;
+            Drawable star = mStarDrawable.getConstantState().newDrawable().mutate();
+            star.setTint(color);
+            star.setBounds(x, padding, x + starWidth, padding + starWidth);
+            star.draw(canvas);
+        }
+        if (partialStarFactor > 0f) {
+            int x = fullStars * cellWidth + padding;
+            ClipDrawable star = new ClipDrawable(mStarDrawable,
+                    Gravity.LEFT, ClipDrawable.HORIZONTAL);
+            star.setTint(color);
+            star.setLevel((int) (MAX_LEVEL * partialStarFactor));
+            star.setBounds(x, padding, x + starWidth, padding + starWidth);
+            star.draw(canvas);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index e4c9be4..7806c98 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,7 +24,6 @@
 import android.annotation.SuppressLint;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -36,6 +35,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 
 import java.util.Arrays;
@@ -259,7 +259,7 @@
             m1.setSaturation(0);
 
             ColorMatrix m2 = new ColorMatrix();
-            setColorScale(color, m2);
+            Themes.setColorScaleOnMatrix(color, m2);
             m1.postConcat(m2);
 
             animateFilterTo(m1.getArray());
@@ -384,11 +384,6 @@
         }
     }
 
-    public static void setColorScale(int color, ColorMatrix target) {
-        target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
-                Color.blue(color) / 255f, Color.alpha(color) / 255f);
-    }
-
     public int getBlurSizeOutline() {
         return mBlurSizeOutline;
     }
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 840fcf5..503c2ec 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -121,6 +121,11 @@
     }
 
     @Override
+    public float getIconSize() {
+        return mIconSize;
+    }
+
+    @Override
     public int maxNumItems() {
         return MAX_NUM_ITEMS_IN_PREVIEW;
     }
@@ -129,24 +134,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 3d28f22..15bdea9 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -46,6 +46,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;
@@ -56,7 +57,6 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LogDecelerateInterpolator;
 import com.android.launcher3.OnAlarmListener;
@@ -67,22 +67,22 @@
 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.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;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 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.anim.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
 
 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.
@@ -135,8 +135,10 @@
 
     @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
 
+    private FolderAnimationManager mFolderAnimationManager;
+
     private final int mExpandDuration;
-    private final int mMaterialExpandDuration;
+    public final int mMaterialExpandDuration;
     private final int mMaterialExpandStagger;
 
     protected final Launcher mLauncher;
@@ -249,8 +251,11 @@
         }
         mFolderName.setOnEditorActionListener(this);
         mFolderName.setSelectAllOnFocus(true);
-        mFolderName.setInputType(mFolderName.getInputType() |
-                InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+        mFolderName.setInputType(mFolderName.getInputType()
+                & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
+                & ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+                | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+        mFolderName.forceDisableSuggestions(true);
 
         mFooter = findViewById(R.id.folder_footer);
 
@@ -474,6 +479,8 @@
                 }
             }
         });
+
+        mFolderAnimationManager = new FolderAnimationManager(this);
     }
 
     /**
@@ -509,51 +516,13 @@
         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);
-        }
-
-        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());
-            }
-        }
-
-        mIsOpen = true;
+    private AnimatorSet getOpeningAnimatorSet() {
+        prepareReveal();
         mFolderIcon.growAndFadeOut();
 
-        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;
-        prepareReveal();
-        centerAboutIcon();
-
         AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
-        int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+
+        int width = getFolderWidth();
         int height = getFolderHeight();
 
         float transX = - 0.075f * (width / 2 - getPivotX());
@@ -594,13 +563,61 @@
         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
+                ? mFolderAnimationManager.getOpeningAnimator()
+                : getOpeningAnimatorSet();
         onCompleteRunnable = new Runnable() {
             @Override
             public void run() {
-                mContent.setLayerType(LAYER_TYPE_NONE, null);
-                mFooter.setLayerType(LAYER_TYPE_NONE, null);
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
@@ -695,7 +712,7 @@
             mFolderName.dispatchBackKey();
         }
 
-        if (mFolderIcon != null) {
+        if (mFolderIcon != null && !FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
             mFolderIcon.shrinkAndFadeIn(animate);
         }
 
@@ -713,12 +730,24 @@
         parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    private AnimatorSet getClosingAnimatorSet() {
+        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
+                ? mFolderAnimationManager.getClosingAnimator()
+                : getClosingAnimatorSet();
+        a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                setLayerType(LAYER_TYPE_NONE, null);
                 closeComplete(true);
             }
             @Override
@@ -730,9 +759,7 @@
                 mState = STATE_ANIMATING;
             }
         });
-        oa.setDuration(mExpandDuration);
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-        oa.start();
+        a.start();
     }
 
     private void closeComplete(boolean wasAnimated) {
@@ -743,10 +770,14 @@
         }
         mDragController.removeDropTarget(this);
         clearFocus();
-        if (wasAnimated) {
-            mFolderIcon.requestFocus();
+        if (mFolderIcon != null) {
+            mFolderIcon.setVisibility(View.VISIBLE);
+            if (wasAnimated) {
+                mFolderIcon.requestFocus();
+            }
         }
 
+
         if (mRearrangeOnClose) {
             rearrangeChildren();
             mRearrangeOnClose = false;
@@ -1046,7 +1077,7 @@
 
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
-        int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+        int width = getFolderWidth();
         int height = getFolderHeight();
 
         float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
@@ -1088,6 +1119,7 @@
         int folderPivotY = height / 2 + (centeredTop - top);
         setPivotX(folderPivotX);
         setPivotY(folderPivotY);
+
         mFolderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() *
                 (1.0f * folderPivotX / width));
         mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
@@ -1119,6 +1151,10 @@
         return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN);
     }
 
+    private int getFolderWidth() {
+        return getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+    }
+
     private int getFolderHeight() {
         return getFolderHeight(getContentAreaHeight());
     }
@@ -1405,6 +1441,11 @@
         updateTextViewFocus();
     }
 
+    @Override
+    public void prepareAutoUpdate() {
+        close(false);
+    }
+
     public void onTitleChanged(CharSequence title) {
     }
 
@@ -1424,6 +1465,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..6ce572d
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.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 FolderIcon.PreviewBackground mPreviewBackground;
+
+    private Context mContext;
+    private Launcher mLauncher;
+
+    private Animator mRevealAnimator;
+    private final TimeInterpolator mOpeningInterpolator;
+    private final TimeInterpolator mClosingInterpolator;
+    private final TimeInterpolator mPreviewItemOpeningInterpolator;
+    private final TimeInterpolator mPreviewItemClosingInterpolator;
+
+    private final FolderIcon.PreviewItemDrawingParams mTmpParams =
+            new FolderIcon.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);
+                }
+            };
+
+    private static final Property<List<BubbleTextView>, Integer> ITEMS_TEXT_COLOR_PROPERTY =
+            new Property<List<BubbleTextView>, Integer>(Integer.class, "textColor") {
+                @Override
+                public Integer get(List<BubbleTextView> items) {
+                    return items.get(0).getCurrentTextColor();
+                }
+
+                @Override
+                public void set(List<BubbleTextView> items, Integer color) {
+                    int size = items.size();
+
+                    for (int i = 0; i < size; ++i) {
+                        items.get(i).setTextColor(color);
+                    }
+                }
+            };
+
+    public FolderAnimationManager(Folder folder) {
+        mFolder = folder;
+        mContent = folder.mContent;
+        mFolderBackground = (GradientDrawable) mFolder.getBackground();
+
+        mFolderIcon = folder.mFolderIcon;
+        mPreviewBackground = mFolderIcon.mBackground;
+
+        mContext = folder.getContext();
+        mLauncher = folder.mLauncher;
+
+        mOpeningInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.folder_opening_interpolator);
+        mClosingInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.folder_closing_interpolator);
+        mPreviewItemOpeningInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.folder_preview_item_opening_interpolator);
+        mPreviewItemClosingInterpolator = AnimationUtils.loadInterpolator(mContext,
+                R.interpolator.folder_preview_item_closing_interpolator);
+    }
+
+    public AnimatorSet getOpeningAnimator() {
+        mFolder.setPivotX(0);
+        mFolder.setPivotY(0);
+
+        AnimatorSet a = getAnimatorSet(true /* isOpening */);
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mFolderIcon.setVisibility(View.INVISIBLE);
+            }
+        });
+        return a;
+    }
+
+    public AnimatorSet getClosingAnimator() {
+        AnimatorSet a = getAnimatorSet(false /* isOpening */);
+        return a;
+    }
+
+    /**
+     * Prepares the Folder for animating between open / closed states.
+     *
+     * @param isOpening If true, return the animator set for the opening animation.
+     */
+    private AnimatorSet getAnimatorSet(final boolean isOpening) {
+        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+        FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+        final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+
+        // Match size/scale of icons in the preview
+        float previewScale = rule.scaleForItem(0, itemsInPreview.size());
+        float previewSize = rule.getIconSize() * previewScale;
+        float folderScale = previewSize / itemsInPreview.get(0).getIconSize();
+
+        final float initialScale = folderScale;
+        final float finalScale = 1f;
+        float scale = isOpening ? initialScale : finalScale;
+        mFolder.setScaleX(scale);
+        mFolder.setScaleY(scale);
+
+        // Match position of the FolderIcon
+        final Rect folderIconPos = new Rect();
+        float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+                .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+        folderScale *= scaleRelativeToDragLayer;
+
+        // 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.
+        final int nudgeOffsetX = (int) (previewSize / 2);
+
+        final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
+                * folderScale);
+        final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
+                * folderScale);
+
+        int initialX = folderIconPos.left + mFolderIcon.mBackground.getOffsetX() - paddingOffsetX
+                - nudgeOffsetX;
+        int initialY = folderIconPos.top + mFolderIcon.mBackground.getOffsetY() - paddingOffsetY;
+        final float xDistance = initialX - lp.x;
+        final float yDistance = initialY - lp.y;
+
+        // Set up the Folder background.
+        final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
+        final int initialColor =
+                ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
+        mFolderBackground.setColor(isOpening ? initialColor : finalColor);
+
+        // Initialize the Folder items' text.
+        final List<BubbleTextView> itemsOnCurrentPage = mFolder.getItemsOnCurrentPage();
+        final int finalTextColor = Themes.getAttrColor(mContext, android.R.attr.textColorSecondary);
+        ITEMS_TEXT_COLOR_PROPERTY.set(itemsOnCurrentPage, isOpening ? Color.TRANSPARENT
+                : finalTextColor);
+
+        // Create the animators.
+        AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+        a.setDuration(mFolder.mMaterialExpandDuration);
+
+        ObjectAnimator translationX = isOpening
+                ? ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_X, xDistance, 0)
+                : ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_X, 0, xDistance);
+        a.play(translationX);
+
+        ObjectAnimator translationY = isOpening
+                ? ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_Y, yDistance, 0)
+                : ObjectAnimator.ofFloat(mFolder, View.TRANSLATION_Y, 0, yDistance);
+        a.play(translationY);
+
+        ObjectAnimator scaleAnimator = isOpening
+                ? ObjectAnimator.ofFloat(mFolder, SCALE_PROPERTY, initialScale, finalScale)
+                : ObjectAnimator.ofFloat(mFolder, SCALE_PROPERTY, finalScale, initialScale);
+        a.play(scaleAnimator);
+
+        ObjectAnimator itemsTextColor = isOpening
+                ? ObjectAnimator.ofArgb(itemsOnCurrentPage, ITEMS_TEXT_COLOR_PROPERTY,
+                        Color.TRANSPARENT, finalTextColor)
+                : ObjectAnimator.ofArgb(itemsOnCurrentPage, ITEMS_TEXT_COLOR_PROPERTY,
+                        finalTextColor, Color.TRANSPARENT);
+        a.play(itemsTextColor);
+
+        ObjectAnimator backgroundColor = isOpening
+                ? ObjectAnimator.ofArgb(mFolderBackground, "color", initialColor, finalColor)
+                : ObjectAnimator.ofArgb(mFolderBackground, "color", finalColor, initialColor);
+        a.play(backgroundColor);
+
+        // Set up the reveal animation that clips the Folder.
+        float stroke = mPreviewBackground.getStrokeWidth();
+        int initialSize = (int) ((mFolderIcon.mBackground.getRadius() * 2 + stroke) / folderScale);
+        int totalOffsetX = paddingOffsetX + Math.round(nudgeOffsetX / folderScale);
+        int unscaledStroke = (int) Math.floor(stroke / folderScale);
+        Rect startRect = new Rect(totalOffsetX + unscaledStroke, unscaledStroke,
+                totalOffsetX + initialSize, initialSize);
+        Rect endRect = new Rect(0, 0, lp.width, lp.height);
+        a.play(getRevealAnimator(isOpening, initialSize / 2f, startRect, endRect));
+
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                ITEMS_TEXT_COLOR_PROPERTY.set(itemsOnCurrentPage, finalTextColor);
+            }
+        });
+
+        // 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(isOpening ? mOpeningInterpolator : mClosingInterpolator);
+        }
+
+        addPreviewItemAnimatorsToSet(a, isOpening, folderScale, nudgeOffsetX);
+        return a;
+    }
+
+    private Animator getRevealAnimator(boolean isOpening, float circleRadius, Rect start,
+            Rect end) {
+        boolean revealIsRunning = mRevealAnimator != null && mRevealAnimator.isRunning();
+        final float finalRadius = revealIsRunning
+                ? ((RoundedRectRevealOutlineProvider) mFolder.getOutlineProvider()).getRadius()
+                : Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+        if (revealIsRunning) {
+            mRevealAnimator.cancel();
+        }
+        mRevealAnimator = new RoundedRectRevealOutlineProvider(circleRadius, finalRadius,
+                start, end).createRevealAnimator(mFolder, !isOpening);
+        return mRevealAnimator;
+    }
+
+    /**
+     * Animate the items that are displayed in the preview.
+     */
+    private void addPreviewItemAnimatorsToSet(AnimatorSet animatorSet, boolean isOpening,
+            final float folderScale, int nudgeOffsetX) {
+        FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+        final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+        final int numItemsInPreview = itemsInPreview.size();
+
+        TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator(isOpening);
+
+        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 = isOpening ? 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 + nudgeOffsetX) / folderScale);
+            final int previewPosY = (int) (mTmpParams.transY / folderScale);
+
+            final float xDistance = previewPosX - btvLp.x;
+            final float yDistance = previewPosY - btvLp.y;
+
+            ObjectAnimator translationX = isOpening
+                    ? ObjectAnimator.ofFloat(btv, View.TRANSLATION_X, xDistance, 0)
+                    : ObjectAnimator.ofFloat(btv, View.TRANSLATION_X, 0, xDistance);
+            translationX.setInterpolator(previewItemInterpolator);
+            animatorSet.play(translationX);
+
+            ObjectAnimator translationY = isOpening
+                    ? ObjectAnimator.ofFloat(btv, View.TRANSLATION_Y, yDistance, 0)
+                    : ObjectAnimator.ofFloat(btv, View.TRANSLATION_Y, 0, yDistance);
+            translationY.setInterpolator(previewItemInterpolator);
+            animatorSet.play(translationY);
+
+            ObjectAnimator scaleAnimator = isOpening
+                    ? ObjectAnimator.ofFloat(btv, SCALE_PROPERTY, initialScale, finalScale)
+                    : ObjectAnimator.ofFloat(btv, SCALE_PROPERTY, finalScale, initialScale);
+            scaleAnimator.setInterpolator(previewItemInterpolator);
+            animatorSet.play(scaleAnimator);
+
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    btv.setTranslationX(0.0f);
+                    btv.setTranslationY(0.0f);
+                    btv.setScaleX(1f);
+                    btv.setScaleY(1f);
+                }
+            });
+        }
+    }
+
+    private TimeInterpolator getPreviewItemInterpolator(boolean isOpening) {
+        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 isOpening ? mPreviewItemOpeningInterpolator : mPreviewItemClosingInterpolator;
+        }
+        return isOpening ? mOpeningInterpolator : mClosingInterpolator;
+    }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6045400..ced9c9e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -24,11 +24,15 @@
 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.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.util.AttributeSet;
@@ -121,12 +125,11 @@
 
     private float mSlop;
 
+    FolderIconPreviewVerifier mPreviewVerifier;
     private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
     private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
     private Drawable mReferenceDrawable = null;
 
-    Paint mBgPaint = new Paint();
-
     private Alarm mOpenAlarm = new Alarm();
 
     private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
@@ -180,11 +183,6 @@
         FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
                 .inflate(resId, group, false);
 
-        // For performance and compatibility reasons we render the preview using a software layer.
-        // In particular, hardware path clipping has spotty ecosystem support and bad performance.
-        // Software rendering also allows us to use shadow layers.
-        icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));
-
         icon.setClipToPadding(false);
         icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
         icon.mFolderName.setText(folderInfo.title);
@@ -223,6 +221,7 @@
 
     private void setFolder(Folder folder) {
         mFolder = folder;
+        mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
         updateItemDrawingParams(false);
     }
 
@@ -409,6 +408,10 @@
         mBadgeInfo = badgeInfo;
     }
 
+    public PreviewLayoutRule getLayoutRule() {
+        return mPreviewLayoutRule;
+    }
+
     /**
      * Sets mBadgeScale to 1 or 0, animating if oldCount or newCount is 0
      * (the badge is being added or removed).
@@ -494,7 +497,7 @@
     }
 
     private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
-        canvas.save();
+        canvas.save(Canvas.MATRIX_SAVE_FLAG);
         canvas.translate(params.transX, params.transY);
         canvas.scale(params.scale, params.scale);
         Drawable d = params.drawable;
@@ -521,10 +524,29 @@
      * 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 Path mClipPath = new Path();
-        private int mStrokeWidth;
+        private float mStrokeWidth;
         private View mInvalidateDelegate;
 
         public int previewSize;
@@ -547,7 +569,7 @@
         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 = 80;
+        private static final int SHADOW_OPACITY = 40;
 
         ValueAnimator mScaleAnimator;
 
@@ -563,7 +585,16 @@
             basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
             basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
 
-            mStrokeWidth = Utilities.pxFromDp(1, dm);
+            // 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();
         }
@@ -593,10 +624,6 @@
         }
 
         void invalidate() {
-            int radius = getScaledRadius();
-            mClipPath.reset();
-            mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);
-
             if (mInvalidateDelegate != null) {
                 mInvalidateDelegate.invalidate();
             }
@@ -611,70 +638,94 @@
             invalidate();
         }
 
-        public void drawBackground(Canvas canvas, Paint paint) {
-            canvas.save();
-            canvas.translate(getOffsetX(), getOffsetY());
-
-            paint.reset();
-            paint.setStyle(Paint.Style.FILL);
-            paint.setXfermode(null);
-            paint.setAntiAlias(true);
-
+        public void drawBackground(Canvas canvas) {
+            mPaint.setStyle(Paint.Style.FILL);
             int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-            paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+            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);
 
-            canvas.drawCircle(radius, radius, radius, paint);
-            canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
+            } else {
+                saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+                clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
+            }
 
-            paint.setStyle(Paint.Style.STROKE);
-            paint.setColor(Color.TRANSPARENT);
-            paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
-            canvas.drawCircle(radius, radius, radius, paint);
+            mShaderMatrix.setScale(shadowRadius, shadowRadius);
+            mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
+            mShadowShader.setLocalMatrix(mShaderMatrix);
+            mPaint.setShader(mShadowShader);
+            canvas.drawPaint(mPaint);
+            mPaint.setShader(null);
 
-            canvas.restore();
+            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, Paint paint) {
-            canvas.save();
-            canvas.translate(getOffsetX(), getOffsetY());
-
-            paint.reset();
-            paint.setAntiAlias(true);
-            paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
-            paint.setStyle(Paint.Style.STROKE);
-            paint.setStrokeWidth(mStrokeWidth);
-
-            float radius = getScaledRadius();
-            canvas.drawCircle(radius, radius, radius - 1, paint);
-
-            canvas.restore();
+        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, Paint paint) {
+        public void drawLeaveBehind(Canvas canvas) {
             float originalScale = mScale;
             mScale = 0.5f;
 
-            canvas.save();
-            canvas.translate(getOffsetX(), getOffsetY());
+            mPaint.setStyle(Paint.Style.FILL);
+            mPaint.setColor(Color.argb(160, 245, 245, 245));
+            drawCircle(canvas, 0 /* deltaRadius */);
 
-            paint.reset();
-            paint.setAntiAlias(true);
-            paint.setColor(Color.argb(160, 245, 245, 245));
-
-            float radius = getScaledRadius();
-            canvas.drawCircle(radius, radius, radius, paint);
-
-            canvas.restore();
             mScale = originalScale;
         }
 
-        // It is the callers responsibility to save and restore the canvas.
-        private void clipCanvas(Canvas canvas) {
-            canvas.translate(getOffsetX(), getOffsetY());
-            canvas.clipPath(mClipPath);
-            canvas.translate(-getOffsetX(), -getOffsetY());
+        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) {
@@ -778,6 +829,14 @@
             };
             animateScale(1f, 1f, onStart, onEnd);
         }
+
+        public int getBackgroundAlpha() {
+            return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        }
+
+        public float getStrokeWidth() {
+            return mStrokeWidth;
+        }
     }
 
     public void setFolderBackground(PreviewBackground bg) {
@@ -794,17 +853,22 @@
         }
 
         if (!mBackground.drawingDelegated()) {
-            mBackground.drawBackground(canvas, mBgPaint);
+            mBackground.drawBackground(canvas);
         }
 
         if (mFolder == null) return;
         if (mFolder.getItemCount() == 0 && !mAnimating) return;
 
-        canvas.save();
+        final int saveCount;
 
-
-        if (mPreviewLayoutRule.clipToBackground()) {
-            mBackground.clipCanvas(canvas);
+        if (canvas.isHardwareAccelerated()) {
+            saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
+                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+        } else {
+            saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+            if (mPreviewLayoutRule.clipToBackground()) {
+                mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT);
+            }
         }
 
         // The items are drawn in coordinates relative to the preview offset
@@ -817,18 +881,24 @@
                 drawPreviewItem(canvas, p);
             }
         }
-        canvas.restore();
+        canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY);
+
+        if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) {
+            mBackground.clipCanvasHardware(canvas);
+        }
+        canvas.restoreToCount(saveCount);
 
         if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
-            mBackground.drawBackgroundStroke(canvas, mBgPaint);
+            mBackground.drawBackgroundStroke(canvas);
         }
 
-        int offsetX = mBackground.getOffsetX();
-        int offsetY = mBackground.getOffsetY();
-        int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
-        Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
         if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
             // If we are animating to the accepting state, animate the badge out.
+            int offsetX = mBackground.getOffsetX();
+            int offsetY = mBackground.getOffsetY();
+            int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
+            Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
+
             float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
             mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
         }
@@ -934,8 +1004,26 @@
         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;
+    }
+
     private void updateItemDrawingParams(boolean animate) {
-        List<View> items = mPreviewLayoutRule.getItemsToDisplay(mFolder);
+        List<BubbleTextView> items = getItemsToDisplay();
         int nItemsInPreview = items.size();
 
         int prevNumItems = mDrawingParams.size();
@@ -950,7 +1038,7 @@
 
         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 (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
                 computePreviewItemDrawingParams(i, nItemsInPreview, p);
@@ -982,6 +1070,10 @@
     }
 
     @Override
+    public void prepareAutoUpdate() {
+    }
+
+    @Override
     public void onAdd(ShortcutInfo item) {
         int oldCount = mBadgeInfo.getNotificationCount();
         mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
@@ -1115,8 +1207,8 @@
             PreviewItemDrawingParams params);
         void init(int availableSpace, int 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 532e5a6..2a6007a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -65,7 +65,7 @@
      */
     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;
 
@@ -116,40 +116,58 @@
     }
 
     /**
-     * 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);
@@ -310,6 +328,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;
@@ -342,7 +362,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();
                 }
             }
@@ -396,12 +416,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() {
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 297203a..9c8c2ef 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -87,6 +87,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 +103,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..492d853 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -29,7 +29,7 @@
 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;
 
 /**
@@ -138,7 +138,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 8b207bb..60bbce4 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -85,10 +85,10 @@
         if (Utilities.isAtLeastO()) {
             try {
                 // Try to load the path from Mask Icon
-                Drawable maskIcon = context.getDrawable(R.drawable.mask_drawable_wrapper);
-                maskIcon.setBounds(0, 0,
+                Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
+                icon.setBounds(0, 0,
                         PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE);
-                return (Path) maskIcon.getClass().getMethod("getIconMask").invoke(maskIcon);
+                return (Path) icon.getClass().getMethod("getIconMask").invoke(icon);
             } catch (Exception e) {
                 Log.e(TAG, "Error loading mask icon", e);
             }
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 23c6a12..cd7cf70 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -19,11 +19,12 @@
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Themes;
 
 /**
@@ -41,12 +42,16 @@
 
     public final int dominantColor;
     public final int backgroundColor;
+    public final ColorMatrixColorFilter backgroundColorMatrixFilter;
     public final int textColor;
     public final int secondaryColor;
 
     private IconPalette(int color) {
         dominantColor = color;
         backgroundColor = getMutedColor(dominantColor);
+        ColorMatrix backgroundColorMatrix = new ColorMatrix();
+        Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
+        backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
         textColor = getTextColorForBackground(backgroundColor);
         secondaryColor = getLowContrastColor(backgroundColor);
     }
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 1a50dfe..ef54661 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -40,7 +40,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -166,7 +165,7 @@
      * @param scale the scale to apply before drawing {@param icon} on the canvas
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
-        icon = wrapToMaskableIconDrawable(context, icon);
+        icon = wrapToAdaptiveIconDrawable(context, icon);
         synchronized (sCanvas) {
             final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
 
@@ -201,7 +200,7 @@
             int textureWidth = iconBitmapSize;
             int textureHeight = iconBitmapSize;
 
-            final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
+            Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
                     Bitmap.Config.ARGB_8888);
             final Canvas canvas = sCanvas;
             canvas.setBitmap(bitmap);
@@ -218,29 +217,39 @@
             icon.setBounds(sOldBounds);
             canvas.setBitmap(null);
 
+            if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO()) {
+                try {
+                    Class clazz = Class.forName("android.graphics.drawable.AdaptiveIconDrawable");
+                    if (clazz.isAssignableFrom(icon.getClass())) {
+                        bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
+                    }
+                } catch (Exception e) {
+                    // do nothing
+                }
+            }
             return bitmap;
         }
     }
 
     /**
-     * If the platform is running O but the app is not providing MaskableIconDrawable, then
+     * If the platform is running O but the app is not providing AdaptiveIconDrawable, then
      * shrink the legacy icon and set it as foreground. Use color drawable as background to
-     * create MaskableIconDrawable.
+     * create AdaptiveIconDrawable.
      */
-    static Drawable wrapToMaskableIconDrawable(Context context, Drawable drawable) {
+    static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable) {
         if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.isAtLeastO())) {
             return drawable;
         }
 
         try {
-            Class clazz = Class.forName("android.graphics.drawable.MaskableIconDrawable");
+            Class clazz = Class.forName("android.graphics.drawable.AdaptiveIconDrawable");
             if (!clazz.isAssignableFrom(drawable.getClass())) {
-                Drawable maskWrapper =
-                        context.getDrawable(R.drawable.mask_drawable_wrapper).mutate();
-                ((FixedScaleDrawable) clazz.getMethod("getForeground").invoke(maskWrapper))
+                Drawable iconWrapper =
+                        context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
+                ((FixedScaleDrawable) clazz.getMethod("getForeground").invoke(iconWrapper))
                         .setDrawable(drawable);
 
-                return maskWrapper;
+                return iconWrapper;
             }
         } catch (Exception e) {
             return drawable;
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 3514a37..22ce098 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -217,6 +217,9 @@
         if (Float.compare(finalProgress, mInternalStateProgress) == 0) {
             return;
         }
+        if (finalProgress < mInternalStateProgress) {
+            shouldAnimate = false;
+        }
         if (!shouldAnimate || mRanFinishAnimation) {
             setInternalProgress(finalProgress);
         } else {
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 31276ec..6c603c9 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -83,6 +83,38 @@
         return result;
     }
 
+    public static Bitmap createCircleWithShadow(int circleColor, int diameter) {
+
+        float shadowRadius = diameter * 1f / 32;
+        float shadowYOffset = diameter * 1f / 16;
+
+        int radius = diameter / 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 center = Math.round(radius + shadowRadius + shadowYOffset);
+        int size = center * 2;
+        Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
+        canvas.setBitmap(result);
+
+        // Draw ambient shadow, center aligned within size
+        blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
+        canvas.drawCircle(center, center, radius, blurPaint);
+
+        // Draw key shadow, bottom aligned within size
+        blurPaint.setAlpha(KEY_SHADOW_ALPHA);
+        canvas.drawCircle(center, center + shadowYOffset, radius, blurPaint);
+
+        // Draw the circle
+        Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        drawPaint.setColor(circleColor);
+        canvas.drawCircle(center, center, radius, drawPaint);
+
+        return result;
+    }
+
     public static ShadowGenerator getInstance(Context context) {
         Preconditions.assertNonUiThread();
         synchronized (LOCK) {
diff --git a/src/com/android/launcher3/logging/DumpTargetWrapper.java b/src/com/android/launcher3/logging/DumpTargetWrapper.java
new file mode 100644
index 0000000..2646a22
--- /dev/null
+++ b/src/com/android/launcher3/logging/DumpTargetWrapper.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.logging;
+
+import android.os.Process;
+import android.text.TextUtils;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+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.model.nano.LauncherDumpProto.UserType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class can be used when proto definition doesn't support nesting.
+ */
+public class DumpTargetWrapper {
+    DumpTarget node;
+    ArrayList<DumpTargetWrapper> children;
+
+    public DumpTargetWrapper() {
+        children = new ArrayList<>();
+    }
+
+    public DumpTargetWrapper(DumpTarget t) {
+        this();
+        node = t;
+    }
+
+    public DumpTargetWrapper(int containerType, int id) {
+        this();
+        node = newContainerTarget(containerType, id);
+    }
+
+    public DumpTargetWrapper(ItemInfo info) {
+        this();
+        node = newItemTarget(info);
+    }
+
+    public DumpTarget getDumpTarget() {
+        return node;
+    }
+
+    public void add(DumpTargetWrapper child) {
+        children.add(child);
+    }
+
+    public List<DumpTarget> getFlattenedList() {
+        ArrayList<DumpTarget> list = new ArrayList<>();
+        list.add(node);
+        if (!children.isEmpty()) {
+            for(DumpTargetWrapper t: children) {
+                list.addAll(t.getFlattenedList());
+            }
+            list.add(node); // add a delimiter empty object
+        }
+        return list;
+    }
+    public DumpTarget newItemTarget(ItemInfo info) {
+        DumpTarget dt = new DumpTarget();
+        dt.type = DumpTarget.Type.ITEM;
+
+        switch (info.itemType) {
+            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                dt.itemType = ItemType.APP_ICON;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                dt.itemType = ItemType.UNKNOWN_ITEMTYPE;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                dt.itemType = ItemType.WIDGET;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                dt.itemType = ItemType.SHORTCUT;
+                break;
+        }
+        return dt;
+    }
+
+    public DumpTarget newContainerTarget(int type, int id) {
+        DumpTarget dt = new DumpTarget();
+        dt.type = DumpTarget.Type.CONTAINER;
+        dt.containerType = type;
+        dt.pageId = id;
+        return dt;
+    }
+
+    public static String getDumpTargetStr(DumpTarget t) {
+        if (t == null){
+            return "";
+        }
+        switch (t.type) {
+            case LauncherDumpProto.DumpTarget.Type.ITEM:
+                return getItemStr(t);
+            case LauncherDumpProto.DumpTarget.Type.CONTAINER:
+                String str = LoggerUtils.getFieldName(t.containerType, ContainerType.class);
+                if (t.containerType == ContainerType.WORKSPACE) {
+                    str += " id=" + t.pageId;
+                } else if (t.containerType == ContainerType.FOLDER) {
+                    str += " grid(" + t.gridX + "," + t.gridY+ ")";
+                }
+                return str;
+            default:
+                return "UNKNOWN TARGET TYPE";
+        }
+    }
+
+    private static String getItemStr(DumpTarget t) {
+        String typeStr = LoggerUtils.getFieldName(t.itemType, ItemType.class);
+        if (!TextUtils.isEmpty(t.packageName)) {
+            typeStr += ", package=" + t.packageName;
+        }
+        if (!TextUtils.isEmpty(t.component)) {
+            typeStr += ", component=" + t.component;
+        }
+        return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+                + "), pageIdx=" + t.pageId + " user=" + t.userType;
+    }
+
+    public DumpTarget writeToDumpTarget(ItemInfo info) {
+        node.component = info.getTargetComponent() == null? "":
+                info.getTargetComponent().flattenToString();
+        node.packageName = info.getIntent() == null? "": info.getIntent().getPackage();
+        node.gridX = info.cellX;
+        node.gridY = info.cellY;
+        node.spanX = info.spanX;
+        node.spanY = info.spanY;
+        node.userType = (info.user.equals(Process.myUserHandle()))? UserType.DEFAULT : UserType.WORK;
+        return node;
+    }
+}
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 c13e8b3..499fdc7 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -1,5 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package com.android.launcher3.logging;
 
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.view.View;
@@ -27,7 +43,7 @@
     private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
     private static final String UNKNOWN = "UNKNOWN";
 
-    private static String getFieldName(int value, Class c) {
+    public static String getFieldName(int value, Class c) {
         SparseArray<String> cache;
         synchronized (sNameCache) {
             cache = sNameCache.get(c);
@@ -68,8 +84,13 @@
             case Target.Type.CONTROL:
                 return getFieldName(t.controlType, ControlType.class);
             case Target.Type.CONTAINER:
-                return getFieldName(t.containerType, ContainerType.class)
-                        + " id=" + t.pageIndex;
+                String str = getFieldName(t.containerType, ContainerType.class);
+                if (t.containerType == ContainerType.WORKSPACE) {
+                    str += " id=" + t.pageIndex;
+                } else if (t.containerType == ContainerType.FOLDER) {
+                    str += " grid(" + t.gridX + "," + t.gridY+ ")";
+                }
+                return str;
             default:
                 return "UNKNOWN TARGET TYPE";
         }
@@ -86,10 +107,8 @@
         if (t.intentHash != 0) {
             typeStr += ", intentHash=" + t.intentHash;
         }
-        if (t.spanX != 0) {
-            typeStr += ", spanX=" + t.spanX;
-        }
-        return typeStr + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
+        return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+                + "), pageIdx=" + t.pageIndex;
     }
 
     public static Target newItemTarget(View v) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 5f45c61..04ca247 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.os.SystemClock;
 import android.util.Log;
@@ -26,9 +27,9 @@
 
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+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;
@@ -59,7 +60,20 @@
 
     private static final String TAG = "UserEvent";
     private static final boolean IS_VERBOSE =
-            ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+            FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+
+    private static UserEventDispatcher sInstance;
+    private static final Object LOCK = new Object();
+
+    public static UserEventDispatcher get(Context context) {
+        synchronized (LOCK) {
+            if (sInstance == null) {
+                sInstance = Utilities.getOverrideObject(UserEventDispatcher.class,
+                        context.getApplicationContext(), R.string.user_event_dispatcher_class);
+            }
+            return sInstance;
+        }
+    }
 
     /**
      * Implemented by containers to provide a container source for a given child.
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 6b64087..930c854 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -24,19 +24,25 @@
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
 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.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.util.ComponentKey;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.MultiHashMap;
+import com.google.protobuf.nano.MessageNano;
 
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -102,21 +108,31 @@
         deepShortcutMap.clear();
     }
 
-    // TODO: current dump is very cryptic and hard to understand. Make it more legible.
-    public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.println(prefix + "Data Model:");
-        for (int i = 0; i < workspaceScreens.size(); i++) {
-            writer.println(prefix + "\tIndex of workspaceScreens:" + workspaceScreens.get(i).toString());
+     public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
+             String[] args) {
+        if (args.length > 0 && TextUtils.equals(args[0], "--proto")) {
+            dumpProto(prefix, fd, writer, args);
+            return;
         }
+        writer.println(prefix + "Data Model:");
+        writer.print(prefix + " ---- workspace screens: ");
+        for (int i = 0; i < workspaceScreens.size(); i++) {
+            writer.print(" " + workspaceScreens.get(i).toString());
+        }
+        writer.println();
+        writer.println(prefix + " ---- workspace items ");
         for (int i = 0; i < workspaceItems.size(); i++) {
             writer.println(prefix + '\t' + workspaceItems.get(i).toString());
         }
+        writer.println(prefix + " ---- appwidget items ");
         for (int i = 0; i < appWidgets.size(); i++) {
             writer.println(prefix + '\t' + appWidgets.get(i).toString());
         }
+        writer.println(prefix + " ---- folder items ");
         for (int i = 0; i< folders.size(); i++) {
             writer.println(prefix + '\t' + folders.valueAt(i).toString());
         }
+        writer.println(prefix + " ---- items id map ");
         for (int i = 0; i< itemsIdMap.size(); i++) {
             writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
         }
@@ -133,6 +149,88 @@
         }
     }
 
+    private synchronized void dumpProto(String prefix, FileDescriptor fd, PrintWriter writer,
+            String[] args) {
+
+        // Add top parent nodes. (L1)
+        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)),
+                    new DumpTargetWrapper(ContainerType.WORKSPACE, i));
+        }
+        DumpTargetWrapper dtw;
+        // Add non leaf / non top nodes (L2)
+        for (int i = 0; i < folders.size(); i++) {
+            FolderInfo fInfo = folders.valueAt(i);
+            dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
+            dtw.writeToDumpTarget(fInfo);
+            for(ShortcutInfo sInfo: fInfo.contents) {
+                DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
+                child.writeToDumpTarget(sInfo);
+                dtw.add(child);
+            }
+            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);
+            }
+        }
+        // Add leaf nodes (L3): *Info
+        for (int i = 0; i < workspaceItems.size(); i++) {
+            ItemInfo info = workspaceItems.get(i);
+            if (info instanceof FolderInfo) {
+                continue;
+            }
+            dtw = new DumpTargetWrapper(info);
+            dtw.writeToDumpTarget(info);
+            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);
+            }
+        }
+        for (int i = 0; i < appWidgets.size(); i++) {
+            ItemInfo info = appWidgets.get(i);
+            dtw = new DumpTargetWrapper(info);
+            dtw.writeToDumpTarget(info);
+            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);
+            }
+        }
+
+
+        // Traverse target wrapper
+        ArrayList<DumpTarget> targetList = new ArrayList<>();
+        targetList.addAll(hotseat.getFlattenedList());
+        for (int i = 0; i < workspaces.size(); i++) {
+            targetList.addAll(workspaces.valueAt(i).getFlattenedList());
+        }
+
+        if (args.length > 1 && TextUtils.equals(args[1], "--debug")) {
+            for (int i = 0; i < targetList.size(); i++) {
+                writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
+            }
+            return;
+        } else {
+            LauncherDumpProto.LauncherImpression proto = new LauncherDumpProto.LauncherImpression();
+            proto.targets = new DumpTarget[targetList.size()];
+            for (int i = 0; i < targetList.size(); i++) {
+                proto.targets[i] = targetList.get(i);
+            }
+            FileOutputStream fos = new FileOutputStream(fd);
+            try {
+
+                fos.write(MessageNano.toByteArray(proto));
+                Log.d(TAG, MessageNano.toByteArray(proto).length + "Bytes");
+            } catch (IOException e) {
+                Log.e(TAG, "Exception writing dumpsys --proto", e);
+            }
+        }
+    }
+
     public synchronized void removeItem(Context context, ItemInfo... items) {
         removeItem(context, Arrays.asList(items));
     }
@@ -142,7 +240,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/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/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 7c98362..278669b 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -63,7 +63,7 @@
 
             for (String pkg : new HashSet<>(entry.getValue())) {
                 if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
-                    if (pmHelper.isAppOnSdcard(pkg)) {
+                    if (pmHelper.isAppOnSdcard(pkg, user)) {
                         packagesUnavailable.add(pkg);
                     } else {
                         packagesRemoved.add(pkg);
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 95c54f7..d2adbde 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -3,9 +3,7 @@
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -19,8 +17,7 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.Preconditions;
 
@@ -79,7 +76,7 @@
             }
             setWidgetsAndShortcuts(widgetsAndShortcuts, context);
         } 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
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 57ec5d1..2e80341 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -21,19 +21,22 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.anim.PropertyResetListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.util.ArrayList;
@@ -41,10 +44,10 @@
 import java.util.List;
 
 /**
- * A {@link LinearLayout} that contains only icons of notifications.
- * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "+x" overflow.
+ * A {@link FrameLayout} that contains only icons of notifications.
+ * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "..." overflow.
  */
-public class NotificationFooterLayout extends LinearLayout {
+public class NotificationFooterLayout extends FrameLayout {
 
     public interface IconAnimationEndListener {
         void onIconAnimationEnd(NotificationInfo animatedNotification);
@@ -56,11 +59,12 @@
 
     private final List<NotificationInfo> mNotifications = new ArrayList<>();
     private final List<NotificationInfo> mOverflowNotifications = new ArrayList<>();
+    private final boolean mRtl;
 
-    LinearLayout.LayoutParams mIconLayoutParams;
+    FrameLayout.LayoutParams mIconLayoutParams;
+    private View mOverflowEllipsis;
     private LinearLayout mIconRow;
     private int mBackgroundColor;
-    private int mTextColor;
 
     public NotificationFooterLayout(Context context) {
         this(context, null, 0);
@@ -73,26 +77,29 @@
     public NotificationFooterLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        int size = getResources().getDimensionPixelSize(
-                R.dimen.notification_footer_icon_size);
-        int padding = getResources().getDimensionPixelSize(
-                R.dimen.deep_shortcut_drawable_padding);
-        mIconLayoutParams = new LayoutParams(size, size);
-        mIconLayoutParams.setMarginStart(padding);
+        Resources res = getResources();
+        mRtl = Utilities.isRtl(res);
+
+        int iconSize = res.getDimensionPixelSize(R.dimen.notification_footer_icon_size);
+        mIconLayoutParams = new LayoutParams(iconSize, iconSize);
         mIconLayoutParams.gravity = Gravity.CENTER_VERTICAL;
+        // Compute margin start for each icon such that the icons between the first one
+        // and the ellipsis are evenly spaced out.
+        int paddingEnd = res.getDimensionPixelSize(R.dimen.notification_footer_icon_row_padding);
+        int ellipsisSpace = res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_offset)
+                + res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_size);
+        int footerWidth = res.getDimensionPixelSize(R.dimen.bg_popup_item_width);
+        int availableIconRowSpace = footerWidth - paddingEnd - ellipsisSpace
+                - iconSize * MAX_FOOTER_NOTIFICATIONS;
+        mIconLayoutParams.setMarginStart(availableIconRowSpace / MAX_FOOTER_NOTIFICATIONS);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mOverflowEllipsis = findViewById(R.id.overflow);
         mIconRow = (LinearLayout) findViewById(R.id.icon_row);
-    }
-
-    public void applyColors(IconPalette iconPalette) {
-        mBackgroundColor = iconPalette.backgroundColor;
-        setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
-        findViewById(R.id.divider).setBackgroundColor(iconPalette.secondaryColor);
-        mTextColor = iconPalette.textColor;
+        mBackgroundColor = ((ColorDrawable) getBackground()).getColor();
     }
 
     /**
@@ -116,41 +123,32 @@
 
         for (int i = 0; i < mNotifications.size(); i++) {
             NotificationInfo info = mNotifications.get(i);
-            addNotificationIconForInfo(info, false /* fromOverflow */);
+            addNotificationIconForInfo(info);
         }
-
-        if (!mOverflowNotifications.isEmpty()) {
-            TextView overflowText = new TextView(getContext());
-            overflowText.setTextColor(mTextColor);
-            updateOverflowText(overflowText);
-            mIconRow.addView(overflowText, mIconLayoutParams);
-        }
+        updateOverflowEllipsisVisibility();
     }
 
-    private void addNotificationIconForInfo(NotificationInfo info, boolean fromOverflow) {
+    private void updateOverflowEllipsisVisibility() {
+        mOverflowEllipsis.setVisibility(mOverflowNotifications.isEmpty() ? GONE : VISIBLE);
+    }
+
+    /**
+     * Creates an icon for the given NotificationInfo, and adds it to the icon row.
+     * @return the icon view that was added
+     */
+    private View addNotificationIconForInfo(NotificationInfo info) {
         View icon = new View(getContext());
         icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
         icon.setOnClickListener(info);
-        int addIndex = mIconRow.getChildCount();
-        if (fromOverflow) {
-            // Add the notification before the overflow view.
-            addIndex--;
-            icon.setAlpha(0);
-            icon.animate().alpha(1);
-        }
         icon.setTag(info);
-        mIconRow.addView(icon, addIndex, mIconLayoutParams);
-    }
-
-    private void updateOverflowText(TextView overflowTextView) {
-        overflowTextView.setText(getResources().getString(R.string.deep_notifications_overflow,
-                mOverflowNotifications.size()));
+        mIconRow.addView(icon, 0, mIconLayoutParams);
+        return icon;
     }
 
     public void animateFirstNotificationTo(Rect toBounds,
             final IconAnimationEndListener callback) {
         AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
-        final View firstNotification = mIconRow.getChildAt(0);
+        final View firstNotification = mIconRow.getChildAt(mIconRow.getChildCount() - 1);
 
         Rect fromBounds = sTempRect;
         firstNotification.getGlobalVisibleRect(fromBounds);
@@ -162,31 +160,56 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 callback.onIconAnimationEnd((NotificationInfo) firstNotification.getTag());
+                removeViewFromIconRow(firstNotification);
             }
         });
         animation.play(moveAndScaleIcon);
 
         // Shift all notifications (not the overflow) over to fill the gap.
         int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginStart();
-        int numIcons = mIconRow.getChildCount()
-                - (mOverflowNotifications.isEmpty() ? 0 : 1);
-        for (int i = 1; i < numIcons; i++) {
+        if (mRtl) {
+            gapWidth = -gapWidth;
+        }
+        if (!mOverflowNotifications.isEmpty()) {
+            NotificationInfo notification = mOverflowNotifications.remove(0);
+            mNotifications.add(notification);
+            View iconFromOverflow = addNotificationIconForInfo(notification);
+            animation.play(ObjectAnimator.ofFloat(iconFromOverflow, ALPHA, 0, 1));
+        }
+        int numIcons = mIconRow.getChildCount() - 1; // All children besides the one leaving.
+        // We have to reset the translation X to 0 when the new main notification
+        // is removed from the footer.
+        PropertyResetListener<View, Float> propertyResetListener
+                = new PropertyResetListener<>(TRANSLATION_X, 0f);
+        for (int i = 0; i < numIcons; i++) {
             final View child = mIconRow.getChildAt(i);
-            Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, -gapWidth);
-            shiftChild.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    // We have to set the translation X to 0 when the new main notification
-                    // is removed from the footer.
-                    // TODO: remove it here instead of expecting trimNotifications to do so.
-                    child.setTranslationX(0);
-                }
-            });
+            Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, gapWidth);
+            shiftChild.addListener(propertyResetListener);
             animation.play(shiftChild);
         }
         animation.start();
     }
 
+    private void removeViewFromIconRow(View child) {
+        mIconRow.removeView(child);
+        mNotifications.remove((NotificationInfo) child.getTag());
+        updateOverflowEllipsisVisibility();
+        if (mIconRow.getChildCount() == 0) {
+            // There are no more icons in the footer, so hide it.
+            PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
+                    Launcher.getLauncher(getContext()));
+            Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
+                    getResources().getInteger(R.integer.config_removeNotificationViewDuration));
+            collapseFooter.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    ((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
+                }
+            });
+            collapseFooter.start();
+        }
+    }
+
     public void trimNotifications(List<String> notifications) {
         if (!isAttachedToWindow() || mIconRow.getChildCount() == 0) {
             return;
@@ -197,43 +220,12 @@
                 overflowIterator.remove();
             }
         }
-        TextView overflowView = null;
         for (int i = mIconRow.getChildCount() - 1; i >= 0; i--) {
             View child = mIconRow.getChildAt(i);
-            if (child instanceof TextView) {
-                overflowView = (TextView) child;
-            } else {
-                NotificationInfo childInfo = (NotificationInfo) child.getTag();
-                if (!notifications.contains(childInfo.notificationKey)) {
-                    mIconRow.removeView(child);
-                    mNotifications.remove(childInfo);
-                    if (!mOverflowNotifications.isEmpty()) {
-                        NotificationInfo notification = mOverflowNotifications.remove(0);
-                        mNotifications.add(notification);
-                        addNotificationIconForInfo(notification, true /* fromOverflow */);
-                    }
-                }
+            NotificationInfo childInfo = (NotificationInfo) child.getTag();
+            if (!notifications.contains(childInfo.notificationKey)) {
+                removeViewFromIconRow(child);
             }
         }
-        if (overflowView != null) {
-            if (mOverflowNotifications.isEmpty()) {
-                mIconRow.removeView(overflowView);
-            } else {
-                updateOverflowText(overflowView);
-            }
-        }
-        if (mIconRow.getChildCount() == 0) {
-            // There are no more icons in the secondary view, so hide it.
-            PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
-                    Launcher.getLauncher(getContext()));
-            int newHeight = getResources().getDimensionPixelSize(
-                    R.dimen.notification_footer_collapsed_height);
-            AnimatorSet collapseSecondary = LauncherAnimUtils.createAnimatorSet();
-            collapseSecondary.play(popup.animateTranslationYBy(getHeight() - newHeight,
-                    getResources().getInteger(R.integer.config_removeNotificationViewDuration)));
-            collapseSecondary.play(LauncherAnimUtils.animateViewHeight(
-                    this, getHeight(), newHeight));
-            collapseSecondary.start();
-        }
     }
 }
diff --git a/src/com/android/launcher3/notification/NotificationHeaderView.java b/src/com/android/launcher3/notification/NotificationHeaderView.java
new file mode 100644
index 0000000..e70b489
--- /dev/null
+++ b/src/com/android/launcher3/notification/NotificationHeaderView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.notification;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+/**
+ * A {@link LinearLayout} that contains two text views: one for the notification count
+ * and one just to say "Notification" or "Notifications"
+ */
+public class NotificationHeaderView extends LinearLayout {
+
+    private TextView mNotificationCount;
+    private TextView mNotificationText;
+
+    public NotificationHeaderView(Context context) {
+        this(context, null, 0);
+    }
+
+    public NotificationHeaderView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationHeaderView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mNotificationCount = (TextView) findViewById(R.id.notification_count);
+        mNotificationText = (TextView) findViewById(R.id.notification_text);
+    }
+
+    public void update(int notificationCount) {
+        mNotificationCount.setText(String.valueOf(notificationCount));
+        mNotificationText.setText(getResources().getQuantityString(
+                R.plurals.notifications_header, notificationCount));
+    }
+}
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index c9b3940..a340742 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -18,30 +18,23 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.animation.LinearInterpolator;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.TextView;
 
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.anim.PillHeightRevealOutlineProvider;
 import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
 import com.android.launcher3.popup.PopupItemView;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.launcher3.LauncherAnimUtils.animateViewHeight;
-
 /**
  * A {@link FrameLayout} that contains a header, main view and a footer.
  * The main view contains the icon and text (title + subtext) of the first notification.
@@ -52,8 +45,7 @@
 
     private static final Rect sTempRect = new Rect();
 
-    private TextView mHeader;
-    private View mDivider;
+    private NotificationHeaderView mHeader;
     private NotificationMainView mMainView;
     private NotificationFooterLayout mFooter;
     private SwipeHelper mSwipeHelper;
@@ -74,11 +66,35 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mHeader = (TextView) findViewById(R.id.header);
-        mDivider = findViewById(R.id.divider);
+        mHeader = (NotificationHeaderView) findViewById(R.id.header);
         mMainView = (NotificationMainView) findViewById(R.id.main_view);
         mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext());
+        mSwipeHelper.setDisableHardwareLayers(true);
+    }
+
+    public Animator animateHeightRemoval(int heightToRemove) {
+        final int newHeight = getHeight() - heightToRemove;
+        Animator heightAnimator = new PillHeightRevealOutlineProvider(mPillRect,
+                getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */);
+        heightAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (newHeight > 0) {
+                    measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
+                            MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
+                    initializeBackgroundClipping(true /* force */);
+                    invalidate();
+                } else {
+                    ((ViewGroup) getParent()).removeView(NotificationItemView.this);
+                }
+            }
+        });
+        return heightAnimator;
+    }
+
+    public void updateHeader(int notificationCount) {
+        mHeader.update(notificationCount);
     }
 
     @Override
@@ -100,13 +116,6 @@
         return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
     }
 
-    @Override
-    protected ColorStateList getAttachedArrowColor() {
-        // This NotificationView itself has a different color that is only
-        // revealed when dismissing notifications.
-        return mFooter.getBackgroundTintList();
-    }
-
     public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
         if (notificationInfos.isEmpty()) {
             return;
@@ -121,15 +130,6 @@
         mFooter.commitNotificationInfos();
     }
 
-    public void applyColors(IconPalette iconPalette) {
-        setBackgroundTintList(ColorStateList.valueOf(iconPalette.secondaryColor));
-        mHeader.setBackgroundTintList(ColorStateList.valueOf(iconPalette.backgroundColor));
-        mHeader.setTextColor(ColorStateList.valueOf(iconPalette.textColor));
-        mDivider.setBackgroundColor(iconPalette.secondaryColor);
-        mMainView.applyColors(iconPalette);
-        mFooter.applyColors(iconPalette);
-    }
-
     public void trimNotifications(final List<String> notificationKeys) {
         boolean dismissedMainNotification = !notificationKeys.contains(
                 mMainView.getNotificationInfo().notificationKey);
@@ -145,12 +145,6 @@
                 public void onIconAnimationEnd(NotificationInfo newMainNotification) {
                     if (newMainNotification != null) {
                         mMainView.applyNotificationInfo(newMainNotification, mIconView, true);
-                        // Remove the animated notification from the footer by calling trim
-                        // TODO: Remove the notification in NotificationFooterLayout directly
-                        // instead of relying on this hack.
-                        List<String> footerNotificationKeys = new ArrayList<>(notificationKeys);
-                        footerNotificationKeys.remove(newMainNotification.notificationKey);
-                        mFooter.trimNotifications(footerNotificationKeys);
                         mMainView.setVisibility(VISIBLE);
                     }
                     mAnimatingNextIcon = false;
@@ -161,27 +155,6 @@
         }
     }
 
-    public Animator createRemovalAnimation(int fullDuration) {
-        AnimatorSet animation = new AnimatorSet();
-        int mainHeight = mMainView.getMeasuredHeight();
-        Animator removeMainView = animateViewHeight(mMainView, mainHeight, 0);
-        removeMainView.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Remove the remaining views but take on their color instead of the darker one.
-                setBackgroundTintList(mHeader.getBackgroundTintList());
-                removeAllViews();
-            }
-        });
-        Animator removeRest = LauncherAnimUtils.animateViewHeight(this, getHeight() - mainHeight, 0);
-        removeMainView.setDuration(fullDuration / 2);
-        removeRest.setDuration(fullDuration / 2);
-        removeMainView.setInterpolator(new LinearInterpolator());
-        removeRest.setInterpolator(new LinearInterpolator());
-        animation.playSequentially(removeMainView, removeRest);
-        return animation;
-    }
-
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
             LauncherLogProto.Target targetParent) {
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 5c16176..d9f7d76 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -26,6 +26,7 @@
 import android.support.v4.util.Pair;
 
 import com.android.launcher3.LauncherModel;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -40,7 +41,7 @@
  * A {@link NotificationListenerService} that sends updates to its
  * {@link NotificationsChangedListener} when notifications are posted or canceled,
  * as well and when this service first connects. An instance of NotificationListener,
- * and its methods for getting notifications, can be obtained via {@link #getInstance()}.
+ * and its methods for getting notifications, can be obtained via {@link #getInstanceIfConnected()}.
  */
 public class NotificationListener extends NotificationListenerService {
 
@@ -50,10 +51,13 @@
 
     private static NotificationListener sNotificationListenerInstance = null;
     private static NotificationsChangedListener sNotificationsChangedListener;
+    private static boolean sIsConnected;
 
     private final Handler mWorkerHandler;
     private final Handler mUiHandler;
 
+    private Ranking mTempRanking = new Ranking();
+
     private Handler.Callback mWorkerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message message) {
@@ -65,8 +69,9 @@
                     mUiHandler.obtainMessage(message.what, message.obj).sendToTarget();
                     break;
                 case MSG_NOTIFICATION_FULL_REFRESH:
-                    final List<StatusBarNotification> activeNotifications
-                            = filterNotifications(getActiveNotifications());
+                    final List<StatusBarNotification> activeNotifications = sIsConnected
+                            ? filterNotifications(getActiveNotifications())
+                            : new ArrayList<StatusBarNotification>();
                     mUiHandler.obtainMessage(message.what, activeNotifications).sendToTarget();
                     break;
             }
@@ -107,10 +112,11 @@
         super();
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper(), mWorkerCallback);
         mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback);
+        sNotificationListenerInstance = this;
     }
 
-    public static @Nullable NotificationListener getInstance() {
-        return sNotificationListenerInstance;
+    public static @Nullable NotificationListener getInstanceIfConnected() {
+        return sIsConnected ? sNotificationListenerInstance : null;
     }
 
     public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
@@ -119,9 +125,8 @@
         }
         sNotificationsChangedListener = listener;
 
-        NotificationListener notificationListener = getInstance();
-        if (notificationListener != null) {
-            notificationListener.onNotificationFullRefresh();
+        if (sNotificationListenerInstance != null) {
+            sNotificationListenerInstance.onNotificationFullRefresh();
         }
     }
 
@@ -132,7 +137,7 @@
     @Override
     public void onListenerConnected() {
         super.onListenerConnected();
-        sNotificationListenerInstance = this;
+        sIsConnected = true;
         onNotificationFullRefresh();
     }
 
@@ -143,7 +148,7 @@
     @Override
     public void onListenerDisconnected() {
         super.onListenerDisconnected();
-        sNotificationListenerInstance = null;
+        sIsConnected = false;
     }
 
     @Override
@@ -164,7 +169,7 @@
         NotificationPostedMsg(StatusBarNotification sbn) {
             packageUserKey = PackageUserKey.fromNotification(sbn);
             notificationKey = sbn.getKey();
-            shouldBeFilteredOut = shouldBeFilteredOut(sbn.getNotification());
+            shouldBeFilteredOut = shouldBeFilteredOut(sbn);
         }
     }
 
@@ -188,14 +193,14 @@
      * Filter out notifications that don't have an intent
      * or are headers for grouped notifications.
      *
-     * TODO: use the system concept of a badged notification instead
+     * @see #shouldBeFilteredOut(StatusBarNotification)
      */
     private List<StatusBarNotification> filterNotifications(
             StatusBarNotification[] notifications) {
         if (notifications == null) return null;
         Set<Integer> removedNotifications = new HashSet<>();
         for (int i = 0; i < notifications.length; i++) {
-            if (shouldBeFilteredOut(notifications[i].getNotification())) {
+            if (shouldBeFilteredOut(notifications[i])) {
                 removedNotifications.add(i);
             }
         }
@@ -209,7 +214,14 @@
         return filteredNotifications;
     }
 
-    private boolean shouldBeFilteredOut(Notification notification) {
+    private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
+        if (Utilities.isAtLeastO()) {
+            getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
+            if (!mTempRanking.canShowBadge()) {
+                return true;
+            }
+        }
+        Notification notification = sbn.getNotification();
         boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
         return (notification.contentIntent == null || isGroupHeader);
     }
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index b342525..bb2dac0 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -16,38 +16,35 @@
 
 package com.android.launcher3.notification;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.LinearLayout;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
 
 /**
- * A {@link LinearLayout} that contains a single notification, e.g. icon + title + text.
+ * A {@link android.widget.FrameLayout} that contains a single notification,
+ * e.g. icon + title + text.
  */
-public class NotificationMainView extends LinearLayout implements SwipeHelper.Callback {
-
-    private final ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
+public class NotificationMainView extends FrameLayout implements SwipeHelper.Callback {
 
     private NotificationInfo mNotificationInfo;
+    private ViewGroup mTextAndBackground;
+    private int mBackgroundColor;
     private TextView mTitleView;
     private TextView mTextView;
-    private IconPalette mIconPalette;
-    private ColorDrawable mColorBackground;
 
     public NotificationMainView(Context context) {
         this(context, null, 0);
@@ -65,14 +62,15 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mTitleView = (TextView) findViewById(R.id.title);
-        mTextView = (TextView) findViewById(R.id.text);
-    }
-
-    public void applyColors(IconPalette iconPalette) {
-        mColorBackground = new ColorDrawable(iconPalette.backgroundColor);
-        setBackground(mColorBackground);
-        mIconPalette = iconPalette;
+        mTextAndBackground = (ViewGroup) findViewById(R.id.text_and_background);
+        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
+        mBackgroundColor = colorBackground.getColor();
+        RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
+                Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
+                colorBackground, null);
+        mTextAndBackground.setBackground(rippleBackground);
+        mTitleView = (TextView) mTextAndBackground.findViewById(R.id.title);
+        mTextView = (TextView) mTextAndBackground.findViewById(R.id.text);
     }
 
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView) {
@@ -84,30 +82,18 @@
      */
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView,
            boolean animate) {
-        if (animate) {
-            mTitleView.setAlpha(0);
-            mTextView.setAlpha(0);
-            mColorBackground.setColor(mIconPalette.secondaryColor);
-        }
         mNotificationInfo = mainNotification;
         mTitleView.setText(mNotificationInfo.title);
         mTextView.setText(mNotificationInfo.text);
-        iconView.setBackground(mNotificationInfo.getIconForBackground(
-                getContext(), mIconPalette.backgroundColor));
+        iconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
+                mBackgroundColor));
         setOnClickListener(mNotificationInfo);
         setTranslationX(0);
         // Add a dummy ItemInfo so that logging populates the correct container and item types
         // instead of DEFAULT_CONTAINERTYPE and DEFAULT_ITEMTYPE, respectively.
         setTag(new ItemInfo());
         if (animate) {
-            AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
-            Animator textFade = ObjectAnimator.ofFloat(mTextView, View.ALPHA, 1);
-            Animator titleFade = ObjectAnimator.ofFloat(mTitleView, View.ALPHA, 1);
-            ValueAnimator colorChange = ObjectAnimator.ofObject(mColorBackground, "color",
-                    mArgbEvaluator, mIconPalette.secondaryColor, mIconPalette.backgroundColor);
-            animation.playTogether(textFade, titleFade, colorChange);
-            animation.setDuration(150);
-            animation.start();
+            ObjectAnimator.ofFloat(mTextAndBackground, ALPHA, 0, 1).setDuration(150).start();
         }
     }
 
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index d34727c..b8d38f5 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -27,14 +27,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Color;
-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.Nullable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -60,19 +58,18 @@
 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.badge.BadgeInfo;
 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.graphics.IconPalette;
 import com.android.launcher3.graphics.TriangleShape;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.shortcuts.ShortcutsItemView;
 import com.android.launcher3.util.PackageUserKey;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -84,18 +81,17 @@
  * A container for shortcuts to deep links within apps.
  */
 @TargetApi(Build.VERSION_CODES.N)
-public class PopupContainerWithArrow extends AbstractFloatingView
-        implements View.OnLongClickListener, View.OnTouchListener, DragSource,
+public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
         DragController.DragListener {
 
-    private final Point mIconShift = new Point();
-    private final Point mIconLastTouchPos = new Point();
-
     protected final Launcher mLauncher;
     private final int mStartDragThreshold;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
     private final boolean mIsRtl;
 
+    public ShortcutsItemView mShortcutsItemView;
+    private NotificationItemView mNotificationItemView;
+
     protected BubbleTextView mOriginalIcon;
     private final Rect mTempRect = new Rect();
     private PointF mInterceptTouchDown = new PointF();
@@ -177,6 +173,8 @@
         boolean reverseOrder = mIsAboveIcon;
         if (reverseOrder) {
             removeAllViews();
+            mNotificationItemView = null;
+            mShortcutsItemView = null;
             itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
             addDummyViews(originalIcon, itemsToPopulate, notificationKeys.length > 1);
 
@@ -184,32 +182,20 @@
             orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
         }
 
-        List<DeepShortcutView> shortcutViews = new ArrayList<>();
-        NotificationItemView notificationView = null;
-        for (int i = 0; i < getChildCount(); i++) {
-            View item = getChildAt(i);
-            switch (itemsToPopulate[i]) {
-                case SHORTCUT:
-                    if (reverseOrder) {
-                        shortcutViews.add(0, (DeepShortcutView) item);
-                    } else {
-                        shortcutViews.add((DeepShortcutView) item);
-                    }
-                    break;
-                case NOTIFICATION:
-                    notificationView = (NotificationItemView) item;
-                    IconPalette iconPalette = originalIcon.getIconPalette();
-                    notificationView.applyColors(iconPalette);
-                    break;
-            }
+        ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
+        List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
+                ? Collections.EMPTY_LIST
+                : mShortcutsItemView.getDeepShortcutViews(reverseOrder);
+        if (mNotificationItemView != null) {
+            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider()
+                    .getBadgeInfoForItem(originalItemInfo);
+            updateNotificationHeader(badgeInfo);
         }
 
         // Add the arrow.
         mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
         mArrow.setPivotX(arrowWidth / 2);
         mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
-        PopupItemView firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-        mArrow.setBackgroundTintList(firstItem.getAttachedArrowColor());
 
         animateOpen();
 
@@ -220,30 +206,47 @@
         // Load the shortcuts on a background thread and update the container as it animates.
         final Looper workerLooper = LauncherModel.getWorkerLooper();
         new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
-                mLauncher, (ItemInfo) originalIcon.getTag(), new Handler(Looper.getMainLooper()),
-                this, shortcutIds, shortcutViews, notificationKeys, notificationView));
+                mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
+                this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView));
     }
 
     private void addDummyViews(BubbleTextView originalIcon,
-            PopupPopulator.Item[] itemsToPopulate, boolean secondaryNotificationViewHasIcons) {
+            PopupPopulator.Item[] itemTypesToPopulate, boolean notificationFooterHasIcons) {
         final Resources res = getResources();
-        final int spacing = res.getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+        final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing);
         final LayoutInflater inflater = mLauncher.getLayoutInflater();
-        int numItems = itemsToPopulate.length;
+        int numItems = itemTypesToPopulate.length;
         for (int i = 0; i < numItems; i++) {
-            final PopupItemView item = (PopupItemView) inflater.inflate(
-                    itemsToPopulate[i].layoutId, this, false);
-            if (itemsToPopulate[i] == PopupPopulator.Item.NOTIFICATION) {
-                int secondaryHeight = secondaryNotificationViewHasIcons ?
-                        res.getDimensionPixelSize(R.dimen.notification_footer_height) :
-                        res.getDimensionPixelSize(R.dimen.notification_footer_collapsed_height);
-                item.findViewById(R.id.footer).getLayoutParams().height = secondaryHeight;
+            PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+            final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
+
+            if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
+                mNotificationItemView = (NotificationItemView) item;
+                int footerHeight = notificationFooterHasIcons ?
+                        res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
+                item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
             }
-            if (i < numItems - 1) {
-                ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
-            }
+
+            boolean itemIsFollowedByDifferentType = i < numItems - 1
+                    && itemTypesToPopulate[i + 1] != itemTypeToPopulate;
+
             item.setAccessibilityDelegate(mAccessibilityDelegate);
-            addView(item);
+            if (itemTypeToPopulate == PopupPopulator.Item.SHORTCUT) {
+                if (mShortcutsItemView == null) {
+                    mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
+                            R.layout.shortcuts_item, this, false);
+                    addView(mShortcutsItemView);
+                }
+                mShortcutsItemView.addDeepShortcutView((DeepShortcutView) item);
+                if (itemIsFollowedByDifferentType) {
+                    ((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing;
+                }
+            } else {
+                addView(item);
+                if (itemIsFollowedByDifferentType) {
+                    ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
+                }
+            }
         }
         // TODO: update this, since not all items are shortcuts
         setContentDescription(getContext().getString(R.string.shortcuts_menu_description,
@@ -535,48 +538,26 @@
         return true;
     }
 
-    @Override
-    public boolean onTouch(View v, MotionEvent ev) {
-        // Touched a shortcut, update where it was touched so we can drag from there on long click.
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_MOVE:
-                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
-                break;
+    /**
+     * Updates the notification header to reflect the badge info. Since this can be called
+     * for any badge info (not necessarily the one associated with this app), we first
+     * check that the ItemInfo matches the one of this popup.
+     */
+    public void updateNotificationHeader(BadgeInfo badgeInfo, ItemInfo originalItemInfo) {
+        if (originalItemInfo != mOriginalIcon.getTag()) {
+            return;
         }
-        return false;
+        updateNotificationHeader(badgeInfo);
     }
 
-    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 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)
-        if (mLauncher.getDragController().isDragging()) return false;
-
-        // Long clicked on a shortcut.
-        mDeferContainerRemoval = true;
-        DeepShortcutView sv = (DeepShortcutView) v.getParent();
-        sv.setWillDrawIcon(false);
-
-        // Move the icon to align with the center-top of the touch point
-        mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
-        mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
-
-        DragView dv = mLauncher.getWorkspace().beginDragShared(
-                sv.getBubbleText(), this, sv.getFinalInfo(),
-                new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
-        dv.animateShift(-mIconShift.x, -mIconShift.y);
-
-        // TODO: support dragging from within folder without having to close it
-        AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
-        return false;
+    private void updateNotificationHeader(BadgeInfo badgeInfo) {
+        if (mNotificationItemView != null && badgeInfo != null) {
+            mNotificationItemView.updateHeader(badgeInfo.getNotificationCount());
+        }
     }
 
     public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
-        final NotificationItemView notificationView = (NotificationItemView) findViewById(R.id.notification_view);
-        if (notificationView == null) {
+        if (mNotificationItemView == null) {
             return;
         }
         ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
@@ -585,12 +566,11 @@
             AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
             final int duration = getResources().getInteger(
                     R.integer.config_removeNotificationViewDuration);
-            final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
-            removeNotification.play(animateTranslationYBy(notificationView.getHeight() + spacing,
-                    duration));
-            Animator reduceHeight = notificationView.createRemovalAnimation(duration);
+            final int spacing = getResources().getDimensionPixelSize(R.dimen.popup_items_spacing);
+            removeNotification.play(reduceNotificationViewHeight(
+                    mNotificationItemView.getHeight() + spacing, duration, mNotificationItemView));
             final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2)
-                    : notificationView;
+                    : mNotificationItemView;
             if (removeMarginView != null) {
                 ValueAnimator removeMargin = ValueAnimator.ofFloat(1, 0).setDuration(duration);
                 removeMargin.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -602,19 +582,16 @@
                 });
                 removeNotification.play(removeMargin);
             }
-            removeNotification.play(reduceHeight);
-            Animator fade = ObjectAnimator.ofFloat(notificationView, ALPHA, 0)
+            Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0)
                     .setDuration(duration);
             fade.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    removeView(notificationView);
+                    removeView(mNotificationItemView);
                     if (getItemCount() == 0) {
                         close(false);
                         return;
                     }
-                    View firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-                    mArrow.setBackgroundTintList(firstItem.getBackgroundTintList());
                 }
             });
             removeNotification.play(fade);
@@ -628,7 +605,7 @@
             removeNotification.start();
             return;
         }
-        notificationView.trimNotifications(badgeInfo.getNotificationKeys());
+        mNotificationItemView.trimNotifications(badgeInfo.getNotificationKeys());
     }
 
     private ObjectAnimator createArrowScaleAnim(float scale) {
@@ -636,16 +613,42 @@
                 mArrow, new PropertyListBuilder().scale(scale).build());
     }
     /**
-     * Animates the translationY of this container if it is open above the icon.
-     * If it is below the icon, the container already shifts up when the height
-     * of a child (e.g. NotificationView) changes, so the translation isn't necessary.
+     * Animates the height of the notification item and the translationY of other items accordingly.
      */
-    public @Nullable Animator animateTranslationYBy(int translationY, int duration) {
-        if (mIsAboveIcon) {
-            return ObjectAnimator.ofFloat(this, TRANSLATION_Y, getTranslationY() + translationY)
-                    .setDuration(duration);
+    public Animator reduceNotificationViewHeight(int heightToRemove, int duration,
+            NotificationItemView notificationItem) {
+        final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
+        AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+        animatorSet.play(notificationItem.animateHeightRemoval(heightToRemove));
+        PropertyResetListener<View, Float> resetTranslationYListener
+                = new PropertyResetListener<>(TRANSLATION_Y, 0f);
+        for (int i = 0; i < getItemCount(); i++) {
+            final PopupItemView itemView = getItemViewAt(i);
+            if (!mIsAboveIcon && itemView == notificationItem) {
+                // The notification view is already in the right place when container is below icon.
+                continue;
+            }
+            ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
+                    itemView.getTranslationY() + translateYBy).setDuration(duration);
+            translateItem.addListener(resetTranslationYListener);
+            animatorSet.play(translateItem);
         }
-        return null;
+        if (mIsAboveIcon) {
+            // All the items, including the notification item, translated down, but the
+            // container itself did not. This means the items would jump back to their
+            // original translation unless we update the container's translationY here.
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    setTranslationY(getTranslationY() + translateYBy);
+                }
+            });
+        }
+        return animatorSet;
+    }
+
+    public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+        return reduceNotificationViewHeight(heightToRemove, duration, mNotificationItemView);
     }
 
     @Override
@@ -677,6 +680,7 @@
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
         // Either the original icon or one of the shortcuts was dragged.
         // Hide the container, but don't remove it yet because that interferes with touch events.
+        mDeferContainerRemoval = true;
         animateClose();
     }
 
@@ -698,7 +702,6 @@
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         target.itemType = ItemType.DEEPSHORTCUT;
-        target.rank = info.rank;
         targetParent.containerType = ContainerType.DEEPSHORTCUTS;
     }
 
@@ -740,38 +743,17 @@
         for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) {
             final PopupItemView view = getItemViewAt(i);
             Animator anim;
-            if (view.willDrawIcon()) {
-                anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
-                int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
-                        : numOpenShortcuts - i - 1;
-                anim.setStartDelay(stagger * animationIndex);
+            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);
-            } else {
-                // The view is being dragged. Animate it such that it collapses with the drag view
-                anim = view.collapseToIcon();
-                anim.setDuration(DragView.VIEW_ZOOM_DURATION);
-
-                // Scale and translate the view to follow the drag view.
-                Point iconCenter = view.getIconCenter();
-                view.setPivotX(iconCenter.x);
-                view.setPivotY(iconCenter.y);
-
-                float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight();
-                Animator anim2 = LauncherAnimUtils.ofPropertyValuesHolder(view,
-                        new PropertyListBuilder()
-                                .scale(scale)
-                                .translationX(mIconShift.x)
-                                .translationY(mIconShift.y)
-                                .build())
-                        .setDuration(DragView.VIEW_ZOOM_DURATION);
-                shortcutAnims.play(anim2);
-            }
+            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) {
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index e314b64..ee2930f 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -172,7 +172,7 @@
     private boolean updateBadgeIcon(BadgeInfo badgeInfo) {
         boolean hadNotificationToShow = badgeInfo.hasNotificationToShow();
         NotificationInfo notificationInfo = null;
-        NotificationListener notificationListener = NotificationListener.getInstance();
+        NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
         if (notificationListener != null && badgeInfo.getNotificationKeys().size() == 1) {
             StatusBarNotification[] activeNotifications = notificationListener
                     .getActiveNotifications(new String[] {badgeInfo.getNotificationKeys().get(0)});
@@ -222,13 +222,13 @@
 
     /** This makes a potentially expensive binder call and should be run on a background thread. */
     public List<StatusBarNotification> getStatusBarNotificationsForKeys(String[] notificationKeys) {
-        NotificationListener notificationListener = NotificationListener.getInstance();
+        NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
         return notificationListener == null ? Collections.EMPTY_LIST
                 : notificationListener.getNotificationsForKeys(notificationKeys);
     }
 
     public void cancelNotification(String notificationKey) {
-        NotificationListener notificationListener = NotificationListener.getInstance();
+        NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
         if (notificationListener == null) {
             return;
         }
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 6af6e7d..eeece88 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -20,9 +20,15 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.Shader;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -30,8 +36,7 @@
 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.util.PillWidthRevealOutlineProvider;
+import com.android.launcher3.anim.PillRevealOutlineProvider;
 
 /**
  * An abstract {@link FrameLayout} that supports animating an item's content
@@ -42,11 +47,14 @@
 
     protected static final Point sTempPoint = new Point();
 
-    private final Rect mPillRect;
+    protected final Rect mPillRect;
     private float mOpenAnimationProgress;
 
     protected View mIconView;
 
+    private final Paint mBackgroundClipPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+            Paint.FILTER_BITMAP_FLAG);
+
     public PopupItemView(Context context) {
         this(context, null, 0);
     }
@@ -73,12 +81,31 @@
         mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
     }
 
-    protected ColorStateList getAttachedArrowColor() {
-        return getBackgroundTintList();
+    protected void initializeBackgroundClipping(boolean force) {
+        if (force || mBackgroundClipPaint.getShader() == null) {
+            mBackgroundClipPaint.setXfermode(null);
+            mBackgroundClipPaint.setShader(null);
+            Bitmap backgroundBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+                    Bitmap.Config.ALPHA_8);
+            Canvas canvas = new Canvas();
+            canvas.setBitmap(backgroundBitmap);
+            canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+                    getBackgroundRadius(), getBackgroundRadius(), mBackgroundClipPaint);
+            Shader backgroundClipShader = new BitmapShader(backgroundBitmap,
+                    Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mBackgroundClipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+            mBackgroundClipPaint.setShader(backgroundClipShader);
+        }
     }
 
-    public boolean willDrawIcon() {
-        return true;
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        initializeBackgroundClipping(false /* force */);
+        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
+                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+        super.dispatchDraw(canvas);
+        canvas.drawPaint(mBackgroundClipPaint);
+        canvas.restoreToCount(saveCount);
     }
 
     /**
@@ -126,17 +153,6 @@
     }
 
     /**
-     * Creates an animator which clips the container to form a circle around the icon.
-     */
-    public Animator collapseToIcon() {
-        int halfHeight = getMeasuredHeight() / 2;
-        int iconCenterX = getIconCenter().x;
-        return new PillWidthRevealOutlineProvider(mPillRect,
-                iconCenterX - halfHeight, iconCenterX + halfHeight)
-                        .createRevealAnimator(this, true);
-    }
-
-    /**
      * Returns the position of the center of the icon relative to the container.
      */
     public Point getIconCenter() {
@@ -147,6 +163,10 @@
         return sTempPoint;
     }
 
+    protected float getBackgroundRadius() {
+        return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
+    }
+
     /**
      * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
      */
@@ -161,10 +181,9 @@
         private final boolean mPivotLeft;
         private final float mTranslateX;
 
-        public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
-                View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
-            super(x, y, pillRect, zoomView.getResources().getDimensionPixelSize(
-                    R.dimen.bg_pill_radius));
+        public ZoomRevealOutlineProvider(int x, int y, Rect pillRect, PopupItemView translateView,
+                View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
+            super(x, y, pillRect, translateView.getBackgroundRadius());
             mTranslateView = translateView;
             mZoomView = zoomView;
             mFullHeight = pillRect.height();
@@ -179,8 +198,10 @@
         public void setProgress(float progress) {
             super.setProgress(progress);
 
-            mZoomView.setScaleX(progress);
-            mZoomView.setScaleY(progress);
+            if (mZoomView != null) {
+                mZoomView.setScaleX(progress);
+                mZoomView.setScaleY(progress);
+            }
 
             float height = mOutline.height();
             mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index d2814ee..39c2db2 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -196,7 +196,8 @@
 
         @Override
         public void run() {
-            mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail, mContainer);
+            mShortcutChild.applyShortcutInfo(mShortcutChildInfo, mDetail,
+                    mContainer.mShortcutsItemView);
         }
     }
 
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b0482f8..3e4cd01 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -37,6 +37,7 @@
 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,7 +47,6 @@
 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;
@@ -112,7 +112,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);
@@ -289,7 +289,7 @@
                 }
 
                 if (insertOperations.size() >= BATCH_INSERT_SIZE) {
-                    mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                    mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                             insertOperations);
                     insertOperations.clear();
                 }
@@ -300,7 +300,7 @@
             throw new Exception("Insufficient data");
         }
         if (!insertOperations.isEmpty()) {
-            mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                     insertOperations);
             insertOperations.clear();
         }
@@ -319,7 +319,7 @@
             mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
 
             if (!insertOperations.isEmpty()) {
-                mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
                         insertOperations);
             }
         }
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 38a3e1f..4dc3c1c 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -40,6 +40,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
@@ -89,7 +90,11 @@
                 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 
             mWrapper = new FrameLayout(getActivity());
-            mWrapper.addView(createQsb(mWrapper));
+
+            // Only add the view when enabled
+            if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+                mWrapper.addView(createQsb(mWrapper));
+            }
             return mWrapper;
         }
 
@@ -197,6 +202,11 @@
         }
 
         private void rebindFragment() {
+            // Exit if the embedded qsb is disabled
+            if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+                return;
+            }
+
             if (mWrapper != null && getActivity() != null) {
                 mWrapper.removeAllViews();
                 mWrapper.addView(createQsb(mWrapper));
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 9413913..df7f695 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -65,7 +65,8 @@
     }
 
     public static boolean supportsShortcuts(ItemInfo info) {
-        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && !info.isDisabled();
     }
 
     public boolean wasLastCallSuccess() {
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 2f07c9a..47a023e 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,26 +17,30 @@
 package com.android.launcher3.shortcuts;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
+import android.widget.FrameLayout;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.popup.PopupItemView;
+import com.android.launcher3.Utilities;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
  * This lets us animate the DeepShortcutView (icon and text) separately from the background.
  */
-public class DeepShortcutView extends PopupItemView {
+public class DeepShortcutView extends FrameLayout {
+
+    private static final Point sTempPoint = new Point();
 
     private final Rect mPillRect;
 
     private DeepShortcutTextView mBubbleText;
+    private View mIconView;
 
     private ShortcutInfo mInfo;
     private ShortcutInfoCompat mDetail;
@@ -59,6 +63,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut);
+        mIconView = findViewById(R.id.icon);
     }
 
     public DeepShortcutTextView getBubbleText() {
@@ -73,6 +78,17 @@
         return mIconView.getVisibility() == View.VISIBLE;
     }
 
+    /**
+     * Returns the position of the center of the icon relative to the container.
+     */
+    public Point getIconCenter() {
+        sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
+        if (Utilities.isRtl(getResources())) {
+            sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
+        }
+        return sTempPoint;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -81,7 +97,7 @@
 
     /** package private **/
     public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
-            PopupContainerWithArrow container) {
+            ShortcutsItemView container) {
         mInfo = info;
         mDetail = detail;
         mBubbleText.applyFromShortcutInfo(info);
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
new file mode 100644
index 0000000..349c4c9
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.ItemInfo;
+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.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupItemView;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link PopupItemView} that contains all of the {@link DeepShortcutView}s for an app.
+ */
+public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener,
+        View.OnTouchListener, LogContainerProvider {
+
+    private Launcher mLauncher;
+    private LinearLayout mDeepShortcutsLayout;
+    private final Point mIconShift = new Point();
+    private final Point mIconLastTouchPos = new Point();
+
+    public ShortcutsItemView(Context context) {
+        this(context, null, 0);
+    }
+
+    public ShortcutsItemView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ShortcutsItemView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mLauncher = Launcher.getLauncher(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDeepShortcutsLayout = (LinearLayout) findViewById(R.id.deep_shortcuts);
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        // Touched a shortcut, update where it was touched so we can drag from there on long click.
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_MOVE:
+                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
+                break;
+        }
+        return false;
+    }
+
+    @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 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)
+        if (mLauncher.getDragController().isDragging()) return false;
+
+        // Long clicked on a shortcut.
+        DeepShortcutView sv = (DeepShortcutView) v.getParent();
+        sv.setWillDrawIcon(false);
+
+        // Move the icon to align with the center-top of the touch point
+        mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
+        mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
+
+        DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getBubbleText(),
+                (PopupContainerWithArrow) getParent(), sv.getFinalInfo(),
+                new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
+        dv.animateShift(-mIconShift.x, -mIconShift.y);
+
+        // TODO: support dragging from within folder without having to close it
+        AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_FOLDER);
+        return false;
+    }
+
+    public void addDeepShortcutView(DeepShortcutView deepShortcutView) {
+        if (getNumDeepShortcuts() > 0) {
+            getDeepShortcutAt(getNumDeepShortcuts() - 1).findViewById(R.id.divider)
+                    .setVisibility(VISIBLE);
+        }
+        mDeepShortcutsLayout.addView(deepShortcutView);
+    }
+
+    private DeepShortcutView getDeepShortcutAt(int index) {
+        return (DeepShortcutView) mDeepShortcutsLayout.getChildAt(index);
+    }
+
+    private int getNumDeepShortcuts() {
+        return mDeepShortcutsLayout.getChildCount();
+    }
+
+    public List<DeepShortcutView> getDeepShortcutViews(boolean reverseOrder) {
+        int numDeepShortcuts = getNumDeepShortcuts();
+        List<DeepShortcutView> deepShortcutViews = new ArrayList<>(numDeepShortcuts);
+        for (int i = 0; i < numDeepShortcuts; i++) {
+            DeepShortcutView deepShortcut = getDeepShortcutAt(i);
+            if (reverseOrder) {
+                deepShortcutViews.add(0, deepShortcut);
+            } else {
+                deepShortcutViews.add(deepShortcut);
+            }
+        }
+        return deepShortcutViews;
+    }
+
+    @Override
+    public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
+        AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
+        openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft));
+        for (int i = 0; i < getNumDeepShortcuts(); i++) {
+            View deepShortcutIcon = getDeepShortcutAt(i).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 < getNumDeepShortcuts(); i++) {
+            View deepShortcutIcon = getDeepShortcutAt(i).getIconView();
+            deepShortcutIcon.setScaleX(1);
+            deepShortcutIcon.setScaleY(1);
+            closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
+                    deepShortcutIcon, new PropertyListBuilder().scale(0).build()));
+        }
+        return closeAnimation;
+    }
+
+    @Override
+    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
+        target.itemType = LauncherLogProto.ItemType.DEEPSHORTCUT;
+        target.rank = info.rank;
+        targetParent.containerType = LauncherLogProto.ContainerType.DEEPSHORTCUTS;
+    }
+}
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 6797c7b..aedca8d 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -180,9 +180,6 @@
         }
 
         @Override
-        public UserEventDispatcher getUserEventDispatcher() { return null; }
-
-        @Override
         public View getQsbBar() {
             return null;
         }
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 577d19f..85a000c 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -21,6 +21,7 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.os.UserHandle;
+import android.support.v4.os.BuildCompat;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
@@ -31,6 +32,7 @@
 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.compat.UserManagerCompat;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -68,12 +70,15 @@
     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) {
@@ -127,7 +132,7 @@
             // 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()) {
+            if (userAppsExisted && !homescreenApps.isEmpty() && mAddIconsToHomescreen) {
                 mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps));
             }
         }
@@ -147,6 +152,13 @@
             }
             // 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;
+            }
             if (mPrefs.contains(folderIdKey)) {
                 long folderId = mPrefs.getLong(folderIdKey, 0);
                 final FolderInfo workFolder = mModel.findFolderById(folderId);
@@ -163,6 +175,7 @@
 
                     @Override
                     public void run() {
+                        workFolder.prepareAutoUpdate();
                         for (ShortcutInfo info : workFolderApps) {
                             workFolder.add(info, false);
                         }
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index e89fc0c..7629f78 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -40,63 +40,55 @@
  */
 public class PackageManagerHelper {
 
-    private static final int FLAG_SUSPENDED = 1<<30;
-
     private final Context mContext;
     private final PackageManager mPm;
+    private final LauncherAppsCompat mLauncherApps;
 
     public PackageManagerHelper(Context context) {
         mContext = context;
         mPm = context.getPackageManager();
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
     }
 
     /**
      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
      * guarantee that the app is on SD card.
      */
-    public boolean isAppOnSdcard(String packageName) {
-        return isAppEnabled(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+    public boolean isAppOnSdcard(String packageName, UserHandle user) {
+        ApplicationInfo info = mLauncherApps.getApplicationInfo(
+                packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, user);
+        return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    public boolean isAppEnabled(String packageName) {
-        return isAppEnabled(packageName, 0);
-    }
-
-    public boolean isAppEnabled(String packageName, int flags) {
-        try {
-            ApplicationInfo info = mPm.getApplicationInfo(packageName, flags);
-            return info != null && info.enabled;
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
-    }
-
-    public boolean isAppSuspended(String packageName) {
-        try {
-            ApplicationInfo info = mPm.getApplicationInfo(packageName, 0);
-            return info != null && isAppSuspended(info);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
+    /**
+     * Returns whether the target app is suspended for a given user as per
+     * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
+     */
+    public boolean isAppSuspended(String packageName, UserHandle user) {
+        ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, 0, user);
+        return info != null && isAppSuspended(info);
     }
 
     public boolean isSafeMode() {
-        return mPm.isSafeMode();
+        return mContext.getPackageManager().isSafeMode();
     }
 
     public Intent getAppLaunchIntent(String pkg, UserHandle user) {
-        List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(mContext)
-                .getActivityList(pkg, user);
+        List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
         return activities.isEmpty() ? null :
                 AppInfo.makeLaunchIntent(mContext, activities.get(0), user);
     }
 
+    /**
+     * Returns whether an application is suspended as per
+     * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
+     */
     public static boolean isAppSuspended(ApplicationInfo info) {
         // The value of FLAG_SUSPENDED was reused by a hidden constant
         // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
         // or later.
         if (Utilities.ATLEAST_NOUGAT) {
-            return (info.flags & FLAG_SUSPENDED) != 0;
+            return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
         } else {
             return false;
         }
diff --git a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
deleted file mode 100644
index 89dda3b..0000000
--- a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
+++ /dev/null
@@ -1,41 +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;
-
-/**
- * Extension of {@link PillRevealOutlineProvider} which only changes the width of the pill.
- */
-public class PillWidthRevealOutlineProvider extends PillRevealOutlineProvider {
-
-    private final int mStartLeft;
-    private final int mStartRight;
-
-    public PillWidthRevealOutlineProvider(Rect pillRect, int left, int right) {
-        super(0, 0, pillRect);
-        mOutline.set(pillRect);
-        mStartLeft = left;
-        mStartRight = right;
-    }
-
-    @Override
-    public void setProgress(float progress) {
-        mOutline.left = (int) (progress * mPillRect.left + (1 - progress) * mStartLeft);
-        mOutline.right = (int) (progress * mPillRect.right + (1 - progress) * mStartRight);
-    }
-}
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 1ff6293..5344416 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/Themes.java b/src/com/android/launcher3/util/Themes.java
index acd589e..d863339 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
 import android.view.ContextThemeWrapper;
 
 /**
@@ -49,4 +51,21 @@
         ta.recycle();
         return (int) (255 * alpha + 0.5f);
     }
+
+    /**
+     * Scales a color matrix such that, when applied to color R G B A, it produces R' G' B' A' where
+     * R' = r * R
+     * G' = g * G
+     * B' = b * B
+     * A' = a * A
+     *
+     * The matrix will, for instance, turn white into r g b a, and black will remain black.
+     *
+     * @param color The color r g b a
+     * @param target The ColorMatrix to scale
+     */
+    public static void setColorScaleOnMatrix(int color, ColorMatrix target) {
+        target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
+                Color.blue(color) / 255f, Color.alpha(color) / 255f);
+    }
 }
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_config/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/BuildConfig.java
similarity index 67%
copy from src_config/com/android/launcher3/config/ProviderConfig.java
copy to src_config/com/android/launcher3/BuildConfig.java
index 491fa65..4df75a1 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/BuildConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.config;
+package com.android.launcher3;
 
-public class ProviderConfig {
-
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
-    public static final boolean IS_DOGFOOD_BUILD = true;
+/**
+ * Config file used by Make. This file is automatically generated when using gradle.
+ */
+public class BuildConfig {
+    public static final String APPLICATION_ID = "com.android.launcher3";
 }
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 77%
rename from src_config/com/android/launcher3/config/FeatureFlags.java
rename to src_flags/com/android/launcher3/config/FeatureFlags.java
index 7459c0c..87e9871 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,7 +31,12 @@
     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;
+
 
     // Feature flag to enable moving the QSB on the 0th screen of the workspace.
     public static final boolean QSB_ON_FIRST_SCREEN = true;
@@ -38,10 +46,12 @@
     public static final boolean PULLDOWN_SEARCH = false;
     // When enabled the status bar may show dark icons based on the top of the wallpaper.
     public static final boolean LIGHT_STATUS_BAR = false;
-    // When enabled allows to use any point on the fast scrollbar to start dragging.
-    public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
     // 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 MaskableIconDrawable} will be wrapped in this class.
     public static final boolean LEGACY_ICON_TREATMENT = false;
+    // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
+    public static final boolean ADAPTIVE_ICON_SHADOW = true;
+    // When enabled, app discovery will be enabled if service is implemented
+    public static final boolean DISCOVERY_ENABLED = false;
 }
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 24882aa..0a29147 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -23,7 +23,9 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
 
-        <receiver android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig">
+        <receiver
+            android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
+            android:label="No Config">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
@@ -31,7 +33,9 @@
                        android:resource="@xml/appwidget_no_config" />
         </receiver>
 
-        <receiver android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig">
+        <receiver
+            android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
+            android:label="With Config">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
             </intent-filter>
@@ -45,5 +49,15 @@
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
             </intent-filter>
         </activity>
+        <activity
+            android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
+            android:label="Test Pin Item"
+            android:icon="@drawable/test_drawable_pin_item">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/tests/res/drawable/test_drawable_pin_item.xml b/tests/res/drawable/test_drawable_pin_item.xml
new file mode 100644
index 0000000..1d07256
--- /dev/null
+++ b/tests/res/drawable/test_drawable_pin_item.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.
+-->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48.0"
+    android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#66000000"
+        android:pathData="M0,24a24,24 0 1,0 48,0a24,24 0 1,0 -48,0"/>
+    <path
+        android:fillColor="#FF1B5E20"
+        android:pathData="M2,24a22,22 0 1,0 44,0a22,22 0 1,0 -44,0"/>
+
+    <group
+        android:translateX="12"
+        android:translateY="12">
+        <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22H12.8V16H18V14L16,12Z"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index d0ba907..883be5a 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;
@@ -178,6 +178,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..13e0986 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -16,7 +16,6 @@
 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;
@@ -24,7 +23,7 @@
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
 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
@@ -102,14 +102,14 @@
         f.setAccessible(true);
         f.set(task, mockModel);
 
-        DeferredHandler mockHandler = mock(DeferredHandler.class);
-        f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
+        Executor mockExecutor = mock(Executor.class);
+        f = BaseModelUpdateTask.class.getDeclaredField("mUiExecutor");
         f.setAccessible(true);
-        f.set(task, mockHandler);
+        f.set(task, mockExecutor);
 
         task.execute(appState, bgDataModel, allAppsList);
         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/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/testcomponent/BaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
new file mode 100644
index 0000000..904590c
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Base activity with utility methods to help automate testing.
+ */
+public class BaseTestingActivity extends Activity implements View.OnClickListener {
+
+    public static final String SUFFIX_COMMAND = "-command";
+    public static final String EXTRA_METHOD = "method";
+    public static final String EXTRA_PARAM = "param_";
+
+    private static final int MARGIN_DP = 20;
+
+    private final String mAction = this.getClass().getName();
+
+    private LinearLayout mView;
+    private int mMargin;
+
+    private final BroadcastReceiver mCommandReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            handleCommand(intent);
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mMargin = Math.round(TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, MARGIN_DP, getResources().getDisplayMetrics()));
+        mView = new LinearLayout(this);
+        mView.setPadding(mMargin, mMargin, mMargin, mMargin);
+        mView.setOrientation(LinearLayout.VERTICAL);
+        setContentView(mView);
+
+        registerReceiver(mCommandReceiver, new IntentFilter(mAction + SUFFIX_COMMAND));
+    }
+
+    protected void addButton(String title, String method) {
+        Button button = new Button(this);
+        button.setText(title);
+        button.setTag(method);
+        button.setOnClickListener(this);
+
+        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+        lp.bottomMargin = mMargin;
+        mView.addView(button, lp);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        sendBroadcast(new Intent(mAction).putExtra(Intent.EXTRA_INTENT, getIntent()));
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mCommandReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onClick(View view) {
+        handleCommand(new Intent().putExtra(EXTRA_METHOD, (String) view.getTag()));
+    }
+
+    private void handleCommand(Intent cmd) {
+        String methodName = cmd.getStringExtra(EXTRA_METHOD);
+        try {
+            Method method = null;
+            for (Method m : this.getClass().getDeclaredMethods()) {
+                if (methodName.equals(m.getName()) &&
+                        !Modifier.isStatic(m.getModifiers()) &&
+                        Modifier.isPublic(m.getModifiers())) {
+                    method = m;
+                    break;
+                }
+            }
+            Object[] args = new Object[method.getParameterTypes().length];
+            Bundle extras = cmd.getExtras();
+            for (int i = 0; i < args.length; i++) {
+                args[i] = extras.get(EXTRA_PARAM + i);
+            }
+            method.invoke(this, args);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Intent getCommandIntent(Class<?> clazz, String method) {
+        return new Intent(clazz.getName() + SUFFIX_COMMAND)
+                .putExtra(EXTRA_METHOD, method);
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java b/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java
new file mode 100644
index 0000000..2a031af
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.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.testcomponent;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.IntentSender;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+/**
+ * Sample activity to request pinning an item.
+ */
+@TargetApi(26)
+public class RequestPinItemActivity extends BaseTestingActivity {
+
+    private PendingIntent mCallback = null;
+    private String mShortcutId;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addButton("Pin Shortcut", "pinShortcut");
+        addButton("Pin Widget without config ", "pinWidgetNoConfig");
+        addButton("Pin Widget with config", "pinWidgetWithConfig");
+    }
+
+    public void setCallback(PendingIntent callback) {
+        mCallback = callback;
+    }
+
+    public void setShortcutId(String id) {
+        mShortcutId = id;
+    }
+
+    public void pinShortcut() {
+        ShortcutManager sm = getSystemService(ShortcutManager.class);
+
+        // Generate icon
+        int r = sm.getIconMaxWidth() / 2;
+        Bitmap icon = Bitmap.createBitmap(r * 2, r * 2, Bitmap.Config.ARGB_8888);
+        Paint p = new Paint();
+        p.setColor(Color.RED);
+        new Canvas(icon).drawCircle(r, r, r, p);
+
+        ShortcutInfo info = new ShortcutInfo.Builder(this, mShortcutId)
+                .setIntent(getPackageManager().getLaunchIntentForPackage(getPackageName()))
+                .setIcon(Icon.createWithBitmap(icon))
+                .setShortLabel("Test shortcut")
+                .build();
+
+        IntentSender callback = mCallback == null ? null : mCallback.getIntentSender();
+        sm.requestPinShortcut(info, callback);
+    }
+
+    public void pinWidgetNoConfig() {
+        requestWidget(new ComponentName(this, AppWidgetNoConfig.class));
+    }
+
+    public void pinWidgetWithConfig() {
+        requestWidget(new ComponentName(this, AppWidgetWithConfig.class));
+    }
+
+    private void requestWidget(ComponentName cn) {
+        AppWidgetManager.getInstance(this).requestPinAppWidget(cn, null, mCallback);
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java b/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java
index c0509bc..d76ad04 100644
--- a/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java
+++ b/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java
@@ -15,50 +15,30 @@
  */
 package com.android.launcher3.testcomponent;
 
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Bundle;
 
 /**
  * Simple activity for widget configuration
  */
-public class WidgetConfigActivity extends Activity {
+public class WidgetConfigActivity extends BaseTestingActivity {
 
     public static final String SUFFIX_FINISH = "-finish";
     public static final String EXTRA_CODE = "code";
-    public static final String EXTRA_INTENT = "intent";
-
-    private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            WidgetConfigActivity.this.setResult(
-                    intent.getIntExtra(EXTRA_CODE, RESULT_CANCELED),
-                    (Intent) intent.getParcelableExtra(EXTRA_INTENT));
-            WidgetConfigActivity.this.finish();
-        }
-    };
-
-    private final String mAction = this.getClass().getName();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        registerReceiver(mFinishReceiver, new IntentFilter(mAction + SUFFIX_FINISH));
+        addButton("Cancel", "clickCancel");
+        addButton("OK", "clickOK");
     }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-        sendBroadcast(new Intent(mAction).putExtra(Intent.EXTRA_INTENT, getIntent()));
+    public void clickCancel() {
+        setResult(RESULT_CANCELED);
+        finish();
     }
 
-    @Override
-    protected void onDestroy() {
-        unregisterReceiver(mFinishReceiver);
-        super.onDestroy();
+    public void clickOK() {
+        setResult(RESULT_OK);
+        finish();
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 3f77bfd..9361750 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -22,6 +22,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        setDefaultLauncher();
 
         mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
index 4bc40c6..47b43f5 100644
--- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -15,10 +15,14 @@
  */
 package com.android.launcher3.ui;
 
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Point;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -46,16 +50,24 @@
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
 import com.android.launcher3.util.ManagedProfileHeuristic;
 
+import java.io.BufferedReader;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
 import java.util.Locale;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Base class for all instrumentation tests providing various utility methods.
  */
 public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
 
+    public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
+    public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
+
     public static final long DEFAULT_UI_TIMEOUT = 3000;
     public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
 
@@ -89,11 +101,14 @@
      * Starts the launcher activity in the target package and returns the Launcher instance.
      */
     protected Launcher startLauncher() {
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+        return (Launcher) getInstrumentation().startActivitySync(getHomeIntent());
+    }
+
+    protected Intent getHomeIntent() {
+        return new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
                 .setPackage(mTargetPackage)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return (Launcher) getInstrumentation().startActivitySync(homeIntent);
     }
 
     /**
@@ -104,16 +119,31 @@
         if (mTargetContext.getPackageManager().checkPermission(
                 mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
                 != PackageManager.PERMISSION_GRANTED) {
-            ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
-                    "appwidget grantbind --package " + mTargetPackage);
-            // Read the input stream fully.
-            FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-            while (fis.read() != -1);
-            fis.close();
+            runShellCommand("appwidget grantbind --package " + mTargetPackage);
         }
     }
 
     /**
+     * Sets the target launcher as default launcher.
+     */
+    protected void setDefaultLauncher() throws IOException {
+        ActivityInfo launcher = mTargetContext.getPackageManager()
+                .queryIntentActivities(getHomeIntent(), 0).get(0).activityInfo;
+        runShellCommand("cmd package set-home-activity " +
+                new ComponentName(launcher.packageName, launcher.name).flattenToString());
+    }
+
+    protected void runShellCommand(String command) throws IOException {
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(command);
+
+        // Read the input stream fully.
+        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        while (fis.read() != -1);
+        fis.close();
+    }
+
+    /**
      * Opens all apps and returns the recycler view
      */
     protected UiObject2 openAllApps() {
@@ -236,8 +266,7 @@
                 @Override
                 public void run() {
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
-                    LauncherAppState.getInstance(mTargetContext).getModel()
-                            .resetLoadedState(true, true);
+                    LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
                 }
             });
         } catch (Throwable t) {
@@ -285,4 +314,35 @@
         String name = mTargetContext.getResources().getResourceEntryName(id);
         return By.res(mTargetPackage, name);
     }
+
+
+    /**
+     * Broadcast receiver which blocks until the result is received.
+     */
+    public class BlockingBroadcastReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch latch = new CountDownLatch(1);
+        private Intent mIntent;
+
+        public BlockingBroadcastReceiver(String action) {
+            mTargetContext.registerReceiver(this, new IntentFilter(action));
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mIntent = intent;
+            latch.countDown();
+        }
+
+        public Intent blockingGetIntent() throws InterruptedException {
+            latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
+            mTargetContext.unregisterReceiver(this);
+            return mIntent;
+        }
+
+        public Intent blockingGetExtraIntent() throws InterruptedException {
+            Intent intent = blockingGetIntent();
+            return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
+        }
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
index c6828f0..ee3a628 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -25,6 +25,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        setDefaultLauncher();
 
         mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
index d573eea..061e865 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -25,6 +25,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        setDefaultLauncher();
 
         mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
                 .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 7cbd292..0b4e34f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -18,10 +18,7 @@
 import android.app.Activity;
 import android.app.Application;
 import android.appwidget.AppWidgetManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -41,8 +38,6 @@
 import com.android.launcher3.widget.WidgetCell;
 
 import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Test to verify widget configuration is properly shown.
@@ -50,9 +45,6 @@
 @LargeTest
 public class AddConfigWidgetTest extends LauncherInstrumentationTestCase {
 
-    public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
-    public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
-
     private LauncherAppWidgetProviderInfo mWidgetInfo;
     private SimpleActivityMonitor mActivityMonitor;
     private MainThreadExecutor mMainThreadExecutor;
@@ -69,6 +61,8 @@
                 .registerActivityLifecycleCallbacks(mActivityMonitor);
         mMainThreadExecutor = new MainThreadExecutor();
         mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
+
+        grantWidgetPermission();
     }
 
     @Override
@@ -126,12 +120,11 @@
         // Verify that the widget id is valid and bound
         assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
 
+        setResult(acceptConfig);
         if (acceptConfig) {
-            setResult(Activity.RESULT_OK);
             assertTrue(Wait.atMost(new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT));
             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
         } else {
-            setResult(Activity.RESULT_CANCELED);
             // Verify that the widget id is deleted.
             assertTrue(Wait.atMost(new Condition() {
                 @Override
@@ -142,10 +135,11 @@
         }
     }
 
-    private void setResult(int resultCode) {
-        String action = WidgetConfigActivity.class.getName() + WidgetConfigActivity.SUFFIX_FINISH;
+    private void setResult(boolean success) {
+
         getInstrumentation().getTargetContext().sendBroadcast(
-                new Intent(action).putExtra(WidgetConfigActivity.EXTRA_CODE, resultCode));
+                WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
+                        success ? "clickOK" : "clickCancel"));
     }
 
     /**
@@ -185,28 +179,17 @@
     /**
      * Broadcast receiver for receiving widget config activity status.
      */
-    private class WidgetConfigStartupMonitor extends BroadcastReceiver {
+    private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver {
 
-        private final CountDownLatch latch = new CountDownLatch(1);
-        private Intent mIntent;
-
-        WidgetConfigStartupMonitor() {
-            getInstrumentation().getTargetContext().registerReceiver(this,
-                    new IntentFilter(WidgetConfigActivity.class.getName()));
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
-            latch.countDown();
+        public WidgetConfigStartupMonitor() {
+            super(WidgetConfigActivity.class.getName());
         }
 
         public int getWidgetId() throws InterruptedException {
-            latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
-            getInstrumentation().getTargetContext().unregisterReceiver(this);
-            assertNotNull(mIntent);
-            assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, mIntent.getAction());
-            int widgetId = mIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+            Intent intent = blockingGetExtraIntent();
+            assertNotNull(intent);
+            assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
+            int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                     LauncherAppWidgetInfo.NO_ID);
             assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID);
             return widgetId;
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index b7e1ca9..3c92c57 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -41,6 +41,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        grantWidgetPermission();
 
         widgetInfo = findWidgetProvider(false /* hasConfigureScreen */);
     }
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/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
new file mode 100644
index 0000000..5ef5ec1
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.ui.widget;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.View;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.testcomponent.AppWidgetNoConfig;
+import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+import com.android.launcher3.testcomponent.RequestPinItemActivity;
+import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.SimpleActivityMonitor;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.widget.WidgetCell;
+
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+/**
+ * Test to verify pin item request flow.
+ */
+@LargeTest
+public class RequestPinItemTest  extends LauncherInstrumentationTestCase {
+
+    private SimpleActivityMonitor mActivityMonitor;
+    private MainThreadExecutor mMainThreadExecutor;
+
+    private String mCallbackAction;
+    private String mShortcutId;
+    private int mAppWidgetId;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        grantWidgetPermission();
+        setDefaultLauncher();
+
+        mActivityMonitor = new SimpleActivityMonitor();
+        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
+                .registerActivityLifecycleCallbacks(mActivityMonitor);
+        mMainThreadExecutor = new MainThreadExecutor();
+
+        mCallbackAction = UUID.randomUUID().toString();
+        mShortcutId = UUID.randomUUID().toString();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        ((Application) getInstrumentation().getTargetContext().getApplicationContext())
+                .unregisterActivityLifecycleCallbacks(mActivityMonitor);
+        super.tearDown();
+    }
+
+    public void testPinWidgetNoConfig() throws Throwable {
+        runTest("pinWidgetNoConfig", true, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                return info instanceof LauncherAppWidgetInfo &&
+                        ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
+                        ((LauncherAppWidgetInfo) info).providerName.getClassName()
+                                .equals(AppWidgetNoConfig.class.getName());
+            }
+        });
+    }
+
+    public void testPinWidgetWithConfig() throws Throwable {
+        runTest("pinWidgetWithConfig", true, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                return info instanceof LauncherAppWidgetInfo &&
+                        ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
+                        ((LauncherAppWidgetInfo) info).providerName.getClassName()
+                                .equals(AppWidgetWithConfig.class.getName());
+            }
+        });
+    }
+
+
+    public void testPinWidgetShortcut() throws Throwable {
+        runTest("pinShortcut", false, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                return info instanceof ShortcutInfo &&
+                        info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
+                        ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
+            }
+        });
+    }
+
+    private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher)
+            throws Throwable {
+        if (!Utilities.isAtLeastO()) {
+            return;
+        }
+        lockRotation(true);
+
+        clearHomescreen();
+        startLauncher();
+
+        // Open all apps and wait for load complete
+        final UiObject2 appsContainer = openAllApps();
+        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Open Pin item activity
+        BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
+                RequestPinItemActivity.class.getName());
+        scrollAndFind(appsContainer, By.text("Test Pin Item")).click();
+        assertNotNull(openMonitor.blockingGetExtraIntent());
+
+        // Set callback
+        PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
+                new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT);
+        mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
+                RequestPinItemActivity.class, "setCallback").putExtra(
+                RequestPinItemActivity.EXTRA_PARAM + "0", callback));
+
+        if (!isWidget) {
+            // Set shortcut id
+            mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
+                    RequestPinItemActivity.class, "setShortcutId").putExtra(
+                    RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId));
+        }
+
+        // call the requested method to start the flow
+        mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
+                RequestPinItemActivity.class, activityMethod));
+        UiObject2 widgetCell = mDevice.wait(
+                Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT);
+        assertNotNull(widgetCell);
+
+        // Accept confirmation:
+        BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
+        mDevice.wait(Until.findObject(By.text(mTargetContext.getString(
+                R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click();
+        Intent result = resultReceiver.blockingGetIntent();
+        assertNotNull(result);
+        mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+        if (isWidget) {
+            assertNotSame(-1, mAppWidgetId);
+        }
+
+        // Go back to home
+        mTargetContext.startActivity(getHomeIntent());
+        assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT));
+    }
+
+    /**
+     * Condition for for an item
+     */
+    private class ItemSearchCondition extends Condition implements Callable<Boolean> {
+
+        private final ItemOperator mOp;
+
+        ItemSearchCondition(ItemOperator op) {
+            mOp = op;
+        }
+
+        @Override
+        public boolean isTrue() throws Throwable {
+            return mMainThreadExecutor.submit(this).get();
+        }
+
+        @Override
+        public Boolean call() throws Exception {
+            // Find the resumed launcher
+            Launcher launcher = null;
+            for (Activity a : mActivityMonitor.resumed) {
+                if (a instanceof Launcher) {
+                    launcher = (Launcher) a;
+                }
+            }
+            if (launcher == null) {
+                return false;
+            }
+            return launcher.getWorkspace().getFirstMatch(mOp) != null;
+        }
+    }
+}