Merge branch 'ub-launcher3-master' into launcher3merge2018-10-29

Test: Presubmit
Change-Id: Ib885ad03c7612747672ca1b2f4b1ea38910fdabc
diff --git a/Android.bp b/Android.bp
index 4b32702..e3dd5e5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -16,7 +16,8 @@
     name: "launcher-aosp-tapl",
     static_libs: [
         "androidx.annotation_annotation",
-        "androidx-test",
+        "androidx.test.runner",
+        "androidx.test.rules",
         "androidx.test.uiautomator_uiautomator",
         "SystemUISharedLib",
     ],
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 5b00b7d..1beaea5 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -122,7 +122,7 @@
                        android:value="true" />
 
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
-            android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+            android:theme="@style/AppItemActivityTheme"
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
             android:label="@string/action_add_to_workspace" >
diff --git a/build.gradle b/build.gradle
index b59f264..1b9df53 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@
         google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.2.0-rc03'
+        classpath 'com.android.tools.build:gradle:3.2.1'
         classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
     }
 }
@@ -16,7 +16,7 @@
 
 android {
     compileSdkVersion 28
-    buildToolsVersion '28.0.2'
+    buildToolsVersion '28.0.3'
 
     defaultConfig {
         minSdkVersion 21
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index b3ed365..4129ae8 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -113,6 +113,7 @@
   TASK_PREVIEW = 15;
   SPLIT_SCREEN_TARGET = 16;
   REMOTE_ACTION_SHORTCUT = 17;
+  APP_USAGE_SETTINGS = 18;
 }
 
 enum TipType {
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index c5a7c05..6f3fa4b 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 8ae493f..f9432ad 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oorsig"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Maak toe"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index c04b618..c203a4f 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -24,6 +24,7 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نظرة عامة"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"إغلاق"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات التي تمّ استخدامها مؤخرًا"</string>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 832ed8c..57ad30d 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Bağlayın"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 6bf38eb..3228de0 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -24,6 +24,7 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvaranje"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index a178df0..ec5095c 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Přehled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žádné nedávné položky"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zavřít"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index d0d629f..13eb0cd 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -19,11 +19,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Delt skærm"</string>
+    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Opdel skærm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fastgør"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversigt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Luk"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index aee8b85..2f50639 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -19,11 +19,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Bildschirm teilen"</string>
+    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Splitscreen (Duden compliant spelling, sorry!) Splitscreenmodus (or spelling variations thereof) Splitscreen öffnen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Übersicht"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Schließen"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Zuletzt aktive Apps"</string>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 7364b82..7c11681 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -24,6 +24,7 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Επισκόπηση"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Κλείσιμο"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index d0dc1e8..e6cb731 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index d0dc1e8..e6cb731 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index d0dc1e8..e6cb731 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 60943cc..c54071f 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikuspegi orokorra"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Itxi"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</string>
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 4a30daa..976d656 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نمای کلی"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"بستن"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامه‌های اخیر"</string>
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index baa8d85..fbc3e48 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -24,6 +24,7 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 787c143..d16d4e8 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ringkasan"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi baru-baru ini"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 192ec44..eda8363 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -24,6 +24,7 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Panoramica"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Chiudi"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 64c35ec..13cb7b4 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"מסכים אחרונים"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"סגירה"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 5f061de..c5f823e 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"დახურვა"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 591418b..1caa220 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ಮುಚ್ಚಿ"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 7a669dd..ffafbdb 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"बंद"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index ae6dc7d..6eab2fc 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ပိတ်ရန်"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးကို ရှင်းရန်"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 2cd3942..78e072b 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Prehľad"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zavrieť"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 00231e1..ef9afe0 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Përmbledhja"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Mbyll"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 4911925..7a78e3b 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Yopish"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 3d4f372..afbc66f 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -24,6 +24,8 @@
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Buka konke"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Vala"</string>
+    <!-- no translation found for accessibility_app_usage_settings (6312864233673544149) -->
+    <skip />
     <string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a76899d..c712703 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -36,6 +36,9 @@
     <!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
     <string name="accessibility_close_task">Close</string>
 
+    <!-- Content description for the recent apps's accessibility option that opens its usage settings. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_app_usage_settings">App usage settings</string>
+
     <!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
     <string name="recents_clear_all">Clear all</string>
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 1eaa8bc..1906286 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
@@ -44,7 +45,7 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        AbstractFloatingView.closeAllOpenViews(launcher);
+        AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
         dispatchWindowStateChanged(launcher);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 0d77bca..8f1d46c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
@@ -74,7 +75,7 @@
     public void onStateEnabled(Launcher launcher) {
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(true);
-        AbstractFloatingView.closeAllOpenViews(launcher);
+        AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java
index 35f46cf..8f33e40 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java
@@ -51,7 +51,8 @@
 
     public StatusBarTouchController(Launcher l) {
         mLauncher = l;
-        mTouchSlop = ViewConfiguration.get(l).getScaledTouchSlop();
+        // Guard against TAPs by increasing the touch slop.
+        mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
         mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
     }
 
@@ -90,6 +91,9 @@
                 mTranslator.processMotionEvent(ev);
                 return true;
             }
+            if (Math.abs(dx) > mTouchSlop) {
+                mCanIntercept = false;
+            }
         }
         return false;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
index e9fac26..eaf4183 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -21,7 +21,11 @@
 import com.android.launcher3.Utilities;
 import com.android.systemui.shared.plugins.PluginEnabler;
 
-public class PluginEnablerImpl implements PluginEnabler {
+import androidx.preference.PreferenceDataStore;
+
+public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
+
+    private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
 
     final private SharedPreferences mSharedPrefs;
 
@@ -31,15 +35,25 @@
 
     @Override
     public void setEnabled(ComponentName component, boolean enabled) {
-        mSharedPrefs.edit().putBoolean(toPrefString(component), enabled).apply();
+        putBoolean(pluginEnabledKey(component), enabled);
     }
 
     @Override
     public boolean isEnabled(ComponentName component) {
-        return mSharedPrefs.getBoolean(toPrefString(component), true);
+        return getBoolean(pluginEnabledKey(component), true);
     }
 
-    private String toPrefString(ComponentName component) {
-        return "PLUGIN_ENABLED_" + component.flattenToString();
+    @Override
+    public void putBoolean(String key, boolean value) {
+        mSharedPrefs.edit().putBoolean(key, value).apply();
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defValue) {
+        return mSharedPrefs.getBoolean(key, defValue);
+    }
+
+    static String pluginEnabledKey(ComponentName cn) {
+        return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
index 8a6aa05..910fa0d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -18,7 +18,6 @@
 import android.os.Looper;
 
 import com.android.launcher3.LauncherModel;
-import com.android.systemui.shared.plugins.PluginEnabler;
 import com.android.systemui.shared.plugins.PluginInitializer;
 
 public class PluginInitializerImpl implements PluginInitializer {
@@ -37,7 +36,7 @@
     }
 
     @Override
-    public PluginEnabler getPluginEnabler(Context context) {
+    public PluginEnablerImpl getPluginEnabler(Context context) {
         return new PluginEnablerImpl(context);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 88c362d..6e7c087 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -14,31 +14,37 @@
 
 package com.android.launcher3.uioverrides.plugins;
 
+import android.content.ComponentName;
 import android.content.Context;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginEnabler;
-import com.android.systemui.shared.plugins.PluginInitializer;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginPrefs;
+
+import java.util.Set;
 
 public class PluginManagerWrapper {
 
     public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
             new MainThreadInitializedObject<>(PluginManagerWrapper::new);
 
+    public static final String PLUGIN_CHANGED = PluginManager.PLUGIN_CHANGED;
+
+    private final Context mContext;
     private final PluginManager mPluginManager;
-    private final PluginEnabler mPluginEnabler;
+    private final PluginEnablerImpl mPluginEnabler;
 
     private PluginManagerWrapper(Context c) {
-        PluginInitializer pluginInitializer  = new PluginInitializerImpl();
+        mContext = c;
+        PluginInitializerImpl pluginInitializer  = new PluginInitializerImpl();
         mPluginManager = new PluginManagerImpl(c, pluginInitializer);
         mPluginEnabler = pluginInitializer.getPluginEnabler(c);
     }
 
-    PluginEnabler getPluginEnabler() {
+    public PluginEnablerImpl getPluginEnabler() {
         return mPluginEnabler;
     }
 
@@ -54,4 +60,19 @@
     public void removePluginListener(PluginListener<? extends Plugin> listener) {
         mPluginManager.removePluginListener(listener);
     }
+
+    public Set<String> getPluginActions() {
+        return new PluginPrefs(mContext).getPluginList();
+    }
+
+    /**
+     * Returns the string key used to store plugin enabled/disabled setting
+     */
+    public static String pluginEnabledKey(ComponentName cn) {
+        return PluginEnablerImpl.pluginEnabledKey(cn);
+    }
+
+    public static boolean hasPlugins(Context context) {
+        return PluginPrefs.hasPlugins(context);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java
deleted file mode 100644
index 3da4f84..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginPreferencesFragment.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.launcher3.uioverrides.plugins;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-
-import com.android.launcher3.R;
-import com.android.systemui.shared.plugins.PluginEnabler;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginPrefs;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * This class is copied from System UI Tuner, except using our PluginEnablerImpl. The reason we
- * can't share a common base class in the shared lib is because the androidx preference dependency
- * interferes with our recyclerview and fragment dependencies.
- */
-public class PluginPreferencesFragment extends PreferenceFragment {
-    public static final String ACTION_PLUGIN_SETTINGS
-            = "com.android.systemui.action.PLUGIN_SETTINGS";
-
-    private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
-
-    private PluginPrefs mPluginPrefs;
-    private PluginEnabler mPluginEnabler;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        getContext().registerReceiver(mReceiver, filter);
-        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
-        getContext().registerReceiver(mReceiver, filter);
-
-        mPluginEnabler = PluginManagerWrapper.INSTANCE.get(getContext()).getPluginEnabler();
-        loadPrefs();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        getContext().unregisterReceiver(mReceiver);
-    }
-
-    private void loadPrefs() {
-        PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getContext());
-        screen.setOrderingAsAdded(false);
-        Context prefContext = getContext();
-        mPluginPrefs = new PluginPrefs(getContext());
-        PackageManager pm = getContext().getPackageManager();
-
-        Set<String> pluginActions = mPluginPrefs.getPluginList();
-        ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>();
-        for (String action : pluginActions) {
-            String name = toName(action);
-            List<ResolveInfo> result = pm.queryIntentServices(
-                    new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS);
-            for (ResolveInfo info : result) {
-                String packageName = info.serviceInfo.packageName;
-                if (!plugins.containsKey(packageName)) {
-                    plugins.put(packageName, new ArraySet<>());
-                }
-                plugins.get(packageName).add(name);
-            }
-        }
-
-        List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{PLUGIN_PERMISSION},
-                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
-        apps.forEach(app -> {
-            if (!plugins.containsKey(app.packageName)) return;
-            SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
-            pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
-            screen.addPreference(pref);
-        });
-        setPreferenceScreen(screen);
-    }
-
-    private String toString(ArraySet<String> plugins) {
-        StringBuilder b = new StringBuilder();
-        for (String string : plugins) {
-            if (b.length() != 0) {
-                b.append(", ");
-            }
-            b.append(string);
-        }
-        return b.toString();
-    }
-
-    private String toName(String action) {
-        String str = action.replace("com.android.systemui.action.PLUGIN_", "");
-        StringBuilder b = new StringBuilder();
-        for (String s : str.split("_")) {
-            if (b.length() != 0) {
-                b.append(' ');
-            }
-            b.append(s.substring(0, 1));
-            b.append(s.substring(1).toLowerCase());
-        }
-        return b.toString();
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            loadPrefs();
-        }
-    };
-
-    private static class PluginPreference extends SwitchPreference {
-        private final boolean mHasSettings;
-        private final PackageInfo mInfo;
-        private final PluginEnabler mPluginEnabler;
-
-        public PluginPreference(Context prefContext, PackageInfo info, PluginEnabler pluginEnabler) {
-            super(prefContext);
-            PackageManager pm = prefContext.getPackageManager();
-            mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
-                    .setPackage(info.packageName), 0) != null;
-            mInfo = info;
-            mPluginEnabler = pluginEnabler;
-            setTitle(info.applicationInfo.loadLabel(pm));
-            setChecked(isPluginEnabled());
-            setWidgetLayoutResource(R.layout.switch_preference_with_settings);
-        }
-
-        private boolean isPluginEnabled() {
-            for (int i = 0; i < mInfo.services.length; i++) {
-                ComponentName componentName = new ComponentName(mInfo.packageName,
-                        mInfo.services[i].name);
-                if (!mPluginEnabler.isEnabled(componentName)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        protected boolean persistBoolean(boolean isEnabled) {
-            boolean shouldSendBroadcast = false;
-            for (int i = 0; i < mInfo.services.length; i++) {
-                ComponentName componentName = new ComponentName(mInfo.packageName,
-                        mInfo.services[i].name);
-
-                if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
-                    mPluginEnabler.setEnabled(componentName, isEnabled);
-                    shouldSendBroadcast = true;
-                }
-            }
-            if (shouldSendBroadcast) {
-                final String pkg = mInfo.packageName;
-                final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
-                        pkg != null ? Uri.fromParts("package", pkg, null) : null);
-                getContext().sendBroadcast(intent);
-            }
-            setChecked(isEnabled);
-            return true;
-        }
-
-        @Override
-        protected void onBindView(View view) {
-            super.onBindView(view);
-            view.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
-            view.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
-            view.findViewById(R.id.settings).setOnClickListener(v -> {
-                ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
-                        new Intent(ACTION_PLUGIN_SETTINGS).setPackage(
-                                mInfo.packageName), 0);
-                if (result != null) {
-                    v.getContext().startActivity(new Intent().setComponent(
-                            new ComponentName(result.activityInfo.packageName,
-                                    result.activityInfo.name)));
-                }
-            });
-            view.setOnLongClickListener(v -> {
-                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
-                intent.setData(Uri.fromParts("package", mInfo.packageName, null));
-                getContext().startActivity(intent);
-                return true;
-            });
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 2331a4e..c809e28 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -40,6 +40,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -537,6 +538,9 @@
 
                 @Override
                 public void finish() { }
+
+                @Override
+                public void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect) { }
             };
         }
 
@@ -613,6 +617,8 @@
         void setHandler(WindowTransformSwipeHandler handler);
 
         void finish();
+
+        void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect);
     }
 
     interface ActivityInitListener {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 5b488ca..f8f0905 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -227,7 +227,7 @@
         public RecentsActivityCommand() {
             mHelper = getActivityControlHelper();
             mCreateTime = SystemClock.elapsedRealtime();
-            mRunningTaskId = mAM.getRunningTask().id;
+            mRunningTaskId = RecentsModel.getRunningTaskId();
 
             // Preload the plan
             mRecentsModel.getTasks(null);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index a9ce5cc..2e4d4d2 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -18,6 +18,7 @@
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
@@ -95,6 +96,15 @@
     }
 
     /**
+     * @return The task id of the running task, or -1 if there is no current running task.
+     */
+    public static int getRunningTaskId() {
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        return runningTask != null ? runningTask.id : -1;
+    }
+
+    /**
      * @return Whether the provided {@param changeId} is the latest recent tasks list id.
      */
     public boolean isTaskListValid(int changeId) {
@@ -133,8 +143,8 @@
         }
 
         // Keep the cache up to date with the latest thumbnails
+        int runningTaskId = RecentsModel.getRunningTaskId();
         mTaskList.getTasks(mThumbnailCache.getCacheSize(), true /* keysOnly */, (tasks) -> {
-            int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().id;
             for (Task task : tasks) {
                 if (task.key.id == runningTaskId) {
                     // Skip the running task, it's not going to have an up-to-date snapshot by the
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 66ce4c3..a8eb321 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -63,7 +63,7 @@
 
     protected T mSystemShortcut;
 
-    protected TaskSystemShortcut(T systemShortcut) {
+    public TaskSystemShortcut(T systemShortcut) {
         super(systemShortcut);
         mSystemShortcut = systemShortcut;
     }
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index c47101b..61a191f 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -108,8 +108,9 @@
         Preconditions.assertUIThread();
 
         // Fetch the thumbnail for this task and put it in the cache
-        mCache.put(task.key, ActivityManagerWrapper.getInstance().getTaskThumbnail(
-                task.key.id, true /* reducedResolution */));
+        updateThumbnailInBackground(task, true /* reducedResolution */, (t) -> {
+            mCache.put(task.key, t.thumbnail);
+        });
     }
 
 
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 6908b89..9ea8884 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -597,6 +597,11 @@
     }
 
     private void updateFinalShiftUi() {
+        if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
+            mLayoutListener.update(mCurrentShift.value > 1, mUiLongSwipeMode,
+                    mClipAnimationHelper.getCurrentRectWithInsets());
+        }
+
         final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
         if (passed != mPassedOverviewThreshold) {
             mPassedOverviewThreshold = passed;
@@ -874,7 +879,6 @@
         mLayoutListener.finish();
         mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
 
-        mRecentsView.setRunningTaskHidden(false);
         mRecentsView.setRunningTaskIconScaledDown(false);
         mQuickScrubController.cancelActiveQuickscrub();
     }
@@ -895,6 +899,7 @@
         if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
             mLauncherTransitionController.setPlayFraction(1);
         }
+        mRecentsView.setRunningTaskHidden(false);
     }
 
     private void switchToScreenshot() {
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 8c84f29..57a0e8f 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -77,10 +77,11 @@
     public final Rect mHomeStackBounds = new Rect();
 
     // The clip rect in source app window coordinates
-    private final Rect mClipRect = new Rect();
+    private final RectF mClipRectF = new RectF();
     private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
     private final Matrix mTmpMatrix = new Matrix();
     private final RectF mTmpRectF = new RectF();
+    private final RectF mCurrentRectWithInsets = new RectF();
 
     private float mTargetScale = 1f;
     private float mOffsetScale = 1f;
@@ -153,12 +154,12 @@
                     mTargetOffset.y  * offsetYProgress);
         }
 
-        mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
-        mClipRect.top = (int) (mSourceWindowClipInsets.top * progress);
-        mClipRect.right = (int)
-                (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress));
-        mClipRect.bottom = (int)
-                (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
+        mClipRectF.left = mSourceWindowClipInsets.left * progress;
+        mClipRectF.top = mSourceWindowClipInsets.top * progress;
+        mClipRectF.right =
+                mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress);
+        mClipRectF.bottom =
+                mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
 
         SurfaceParams[] params = new SurfaceParams[targetSet.unfilteredApps.length];
         for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
@@ -166,11 +167,12 @@
             mTmpMatrix.setTranslate(app.position.x, app.position.y);
             Rect crop = app.sourceContainerBounds;
             float alpha = 1f;
+            int layer;
             if (app.mode == targetSet.targetMode) {
                 if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
                     mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
                     mTmpMatrix.postTranslate(app.position.x, app.position.y);
-                    crop = mClipRect;
+                    mClipRectF.roundOut(crop);
                 }
 
                 if (app.isNotInRecents
@@ -179,17 +181,22 @@
                 }
 
                 alpha = mTaskAlphaCallback.apply(app, alpha);
+                layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
             } else {
                 crop = null;
+                layer = Integer.MAX_VALUE;
             }
-
-            params[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop,
-                    RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers));
+            params[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer);
         }
         applyParams(syncTransactionApplier, params);
         return currentRect;
     }
 
+    public RectF getCurrentRectWithInsets() {
+        mTmpMatrix.mapRect(mCurrentRectWithInsets, mClipRectF);
+        return mCurrentRectWithInsets;
+    }
+
     private void applyParams(@Nullable SyncRtSurfaceTransactionApplier syncTransactionApplier,
             SurfaceParams[] params) {
         if (syncTransactionApplier != null) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
index 61740d7..c12a579 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
@@ -15,11 +15,17 @@
  */
 package com.android.quickstep.views;
 
-import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.view.MotionEvent;
+import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Insettable;
@@ -34,12 +40,28 @@
         implements Insettable, LayoutListener {
 
     private final Launcher mLauncher;
+    private final Paint mPaint = new Paint();
     private WindowTransformSwipeHandler mHandler;
+    private RectF mCurrentRect;
 
     public LauncherLayoutListener(Launcher launcher) {
         super(launcher, null);
         mLauncher = launcher;
-        setVisibility(INVISIBLE);
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+        setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+    }
+
+    @Override
+    public void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect) {
+        if (shouldFinish) {
+            finish();
+            return;
+        }
+
+        mCurrentRect = currentRect;
+
+        setWillNotDraw(mCurrentRect == null || isLongSwipe);
+        invalidate();
     }
 
     @Override
@@ -81,11 +103,6 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(1, 1);
-    }
-
-    @Override
     public void logActionCommand(int command) {
         // We should probably log the weather
     }
@@ -97,8 +114,13 @@
 
     @Override
     public void finish() {
-        setHandler(null);
         close(false);
+        setHandler(null);
         mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
     }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawRect(mCurrentRect, mPaint);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index da5b79a..e48a05a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -26,7 +26,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
@@ -43,8 +45,10 @@
 import android.widget.Toast;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.RecentsModel;
@@ -100,7 +104,7 @@
                 }
             };
 
-    private static FloatProperty<TaskView> FOCUS_TRANSITION =
+    private static final FloatProperty<TaskView> FOCUS_TRANSITION =
             new FloatProperty<TaskView>("focusTransition") {
         @Override
         public void setValue(TaskView taskView, float v) {
@@ -113,6 +117,9 @@
         }
     };
 
+    static final Intent SEE_TIME_IN_APP_TEMPLATE =
+            new Intent("com.android.settings.action.TIME_SPENT_IN_APP");
+
     private final OnAttachStateChangeListener mTaskMenuStateListener =
             new OnAttachStateChangeListener() {
                 @Override
@@ -142,6 +149,8 @@
     private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
     private TaskIconCache.IconLoadRequest mIconLoadRequest;
 
+    private long mAppRemainingTimeMs = -1;
+
     public TaskView(Context context) {
         this(context, null);
     }
@@ -195,6 +204,10 @@
         return mSnapshotView.getTaskOverlay();
     }
 
+    private boolean hasRemainingTime() {
+        return mAppRemainingTimeMs > 0;
+    }
+
     public void launchTask(boolean animate) {
         launchTask(animate, (result) -> {
             if (!result) {
@@ -421,6 +434,13 @@
             }
         }
 
+        if (hasRemainingTime()) {
+            info.addAction(
+                    new AccessibilityNodeInfo.AccessibilityAction(
+                            R.string.accessibility_app_usage_settings,
+                            getContext().getText(R.string.accessibility_app_usage_settings)));
+        }
+
         final RecentsView recentsView = getRecentsView();
         final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
                 AccessibilityNodeInfo.CollectionItemInfo.obtain(
@@ -437,6 +457,11 @@
             return true;
         }
 
+        if (action == R.string.accessibility_app_usage_settings) {
+            openAppUsageSettings();
+            return true;
+        }
+
         final List<TaskSystemShortcut> shortcuts =
                 mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
         final int count = shortcuts.size();
@@ -455,6 +480,22 @@
         return super.performAccessibilityAction(action, arguments);
     }
 
+    private void openAppUsageSettings() {
+        final Intent intent = new Intent(SEE_TIME_IN_APP_TEMPLATE)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME,
+                        mTask.getTopComponent().getPackageName()).addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        try {
+            final Launcher launcher = Launcher.getLauncher(getContext());
+            launcher.startActivity(intent);
+            launcher.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
+                    LauncherLogProto.ControlType.APP_USAGE_SETTINGS, this);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to open app usage settings for task "
+                    + mTask.getTopComponent().getPackageName(), e);
+        }
+    }
+
     private RecentsView getRecentsView() {
         return (RecentsView) getParent();
     }
diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/res/drawable-v26/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..3fa8506
--- /dev/null
+++ b/res/drawable-v26/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="?attr/popupColorSecondary"/>
+    <foreground android:drawable="?attr/popupColorSecondary"/>
+</adaptive-icon>
diff --git a/res/drawable/deep_shortcuts_text_placeholder.xml b/res/drawable/deep_shortcuts_text_placeholder.xml
new file mode 100644
index 0000000..99da50f
--- /dev/null
+++ b/res/drawable/deep_shortcuts_text_placeholder.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?attr/popupColorSecondary" />
+    <corners android:radius="8dp" />
+    <size android:height="16dp" />
+</shape>
diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..85a9694
--- /dev/null
+++ b/res/drawable/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <solid android:color="?attr/popupColorSecondary" />
+    <size
+        android:height="32dp"
+        android:width="32dp" />
+</shape>
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 6c316e6..830255b 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -44,7 +44,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:background="?android:attr/colorPrimaryDark"
-                android:theme="@style/WidgetContainerTheme">
+                android:theme="?attr/widgetsTheme">
 
                 <com.android.launcher3.dragndrop.LivePreviewWidgetCell
                     android:id="@+id/widget_cell"
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4a2ad42..92f70e6 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -43,7 +43,8 @@
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="@dimen/deep_shortcut_icon_size"
         android:layout_marginStart="@dimen/popup_padding_start"
-        android:layout_gravity="start|center_vertical" />
+        android:layout_gravity="start|center_vertical"
+        android:background="@drawable/ic_deepshortcut_placeholder"/>
 
     <View
         android:id="@+id/divider"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 70b743f..56d198c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Kennisgewings en programme is af"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Maak toe"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Toe"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Misluk: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 1a1b53a..bebc7b3 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -149,4 +149,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"الإشعارات والتطبيقات متوقفة."</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"إغلاق"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"تمّ الإغلاق"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 573971e..f8aba17 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bağlayın"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bağlıdır"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index baaed88..1e2b267 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -146,4 +146,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 256b0c9..f26226c 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -147,4 +147,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Oznámení a aplikace jsou vypnuty"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavřít"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavřeno"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c30acad..6b78770 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -145,4 +145,6 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Underretninger og apps er slået fra"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Luk"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</string>
+    <!-- no translation found for remote_action_failed (1383965239183576790) -->
+    <skip />
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index d8b6064..20f2a9c 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -145,4 +145,6 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Benachrichtigungen und Apps sind deaktiviert"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Schließen"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Geschlossen"</string>
+    <!-- no translation found for remote_action_failed (1383965239183576790) -->
+    <skip />
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 00717c5..eef66ff 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Κλείσιμο"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Κλειστή"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 181daef..0494eb4 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 181daef..0494eb4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 181daef..0494eb4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 4b2bc3c..29d96f8 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Itxi"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Itxita"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index ff7e749..e78af15 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"اعلان‌ها و برنامه‌ها خاموش هستند"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بستن"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بسته‌شده"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 1f289a8..a4b4028 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -146,4 +146,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Obavijesti i aplikacije isključeni su"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 1ae2f28..a4c6a54 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikasi dan aplikasi nonaktif"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 2f393ed..1c53c75 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Le notifiche e le app non sono attive"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Chiudi"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Chiusa"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7de4388..a62cd90 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -39,7 +39,7 @@
     <string name="all_apps_loading_message" msgid="5813968043155271636">"טוען אפליקציות…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"חפש אפליקציות נוספות"</string>
-    <string name="notifications_header" msgid="1404149926117359025">"הודעות"</string>
+    <string name="notifications_header" msgid="1404149926117359025">"התראות"</string>
     <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"כדי להוסיף קיצור דרך, יש לגעת בו ולהחזיק אותו."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, יש להקיש על קיצור הדרך פעמיים ולהחזיק אותו."</string>
     <string name="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
@@ -91,7 +91,7 @@
     <string name="title_missing_notification_access" msgid="7503287056163941064">"נדרשת גישה להתראות"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"כדי להציג את סימני ההתראות,יש להפעיל התראות מהאפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"שנה את ההגדרות"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"הצגה של סימן ההודעות"</string>
+    <string name="icon_badging_service_title" msgid="2309733118428242174">"הצגה של סימן ההתראות"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"הוספת סמל במסך דף הבית"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"שינוי הצורה של הסמלים"</string>
@@ -147,4 +147,6 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"הודעות ואפליקציות כבויות"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"סגירה"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"סגור"</string>
+    <!-- no translation found for remote_action_failed (1383965239183576790) -->
+    <skip />
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 0199dea..8b4503e 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"შეტყობინებები და აპები გამორთულია"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"დახურვა"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"დახურული"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index fac6ef9..91b08dc9 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ಮುಚ್ಚಿ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ಮುಚ್ಚಲಾಗಿದೆ"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index f36b7d5..df863c1 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -145,4 +145,6 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना आणि अॅप्स बंद आहेत"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करा"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद केले"</string>
+    <!-- no translation found for remote_action_failed (1383965239183576790) -->
+    <skip />
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d5188c9..0b07e30 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ပိတ်ရန်"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ပိတ်ထားသည်"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-v19/styles.xml b/res/values-night-v26/styles.xml
similarity index 70%
copy from res/values-v19/styles.xml
copy to res/values-night-v26/styles.xml
index 36c0971..510e1f4 100644
--- a/res/values-v19/styles.xml
+++ b/res/values-night-v26/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2016 The Android Open Source Project
+* Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
 * limitations under the License.
 */
 -->
+
 <resources>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
+    <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+        <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index ff089b5..c983fe1 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -147,4 +147,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Upozornenia a aplikácie sú vypnuté"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavrieť"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavreté"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 2416780..c26b7a5 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Njoftimet dhe aplikacionet janë joaktive"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Mbyll"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Mbyllur"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index be7cbf6..c3b3dde 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirishnomalar va ilovalar faol emas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Yopish"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Yopiq"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
deleted file mode 100644
index 927719c..0000000
--- a/res/values-v21/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2016 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-<resources>
-
-    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
-        <item name="android:windowTranslucentStatus">false</item>
-        <item name="android:windowTranslucentNavigation">false</item>
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <item name="android:statusBarColor">#00000000</item>
-        <item name="android:navigationBarColor">#00000000</item>
-    </style>
-</resources>
diff --git a/res/values-v19/styles.xml b/res/values-v22/styles.xml
similarity index 71%
rename from res/values-v19/styles.xml
rename to res/values-v22/styles.xml
index 36c0971..f86db7a 100644
--- a/res/values-v19/styles.xml
+++ b/res/values-v22/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2016 The Android Open Source Project
+* Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,11 +16,11 @@
 * limitations under the License.
 */
 -->
+
 <resources>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
+    <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 9283a13..c25319e 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -145,4 +145,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Izaziso nezinhlelo zokusebenza kuvaliwe"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Vala"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Kuvaliwe"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 946afec..5e83ab7 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -19,7 +19,7 @@
     <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end</string>
 
     <!-- String representing the fragment class for settings activity.-->
-    <string name="settings_fragment_name" translatable="false">com.android.launcher3.SettingsActivity$LauncherSettingsFragment</string>
+    <string name="settings_fragment_name" translatable="false">com.android.launcher3.settings.SettingsActivity$LauncherSettingsFragment</string>
 
     <!-- Values for icon shape overrides. These should correspond to entries defined
      in icon_shape_override_paths_names -->
@@ -105,7 +105,10 @@
     <item type="id" name="view_unhighlight_background" />
     <item type="id" name="view_highlighted" />
 
-<!-- Popup items -->
+    <!-- Menu id for feature flags -->
+    <item type="id" name="menu_apply_flags" />
+
+    <!-- Popup items -->
     <integer name="config_popupOpenCloseDuration">150</integer>
     <integer name="config_popupArrowOpenCloseDuration">40</integer>
     <integer name="config_removeNotificationViewDuration">300</integer>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 098aac5..e6791aa 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -27,7 +27,7 @@
         <item name="android:colorEdgeEffect">#FF757575</item>
     </style>
 
-    <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
         <item name="allAppsScrimColor">#EAFFFFFF</item>
         <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
@@ -44,9 +44,13 @@
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
         <item name="folderBadgeColor">?android:attr/colorPrimary</item>
         <item name="loadingIconColor">#FFF</item>
-    </style>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs"></style>
+        <item name="android:windowTranslucentStatus">false</item>
+        <item name="android:windowTranslucentNavigation">false</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
+    </style>
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
         <item name="workspaceTextColor">#FF212121</item>
@@ -94,6 +98,10 @@
     <style name="AppTheme.Dark" parent="@style/LauncherTheme.Dark" />
     <style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
 
+    <style name="AppItemActivityTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
+        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+    </style>
+
     <!--
     Theme overrides to element on homescreen, i.e., which are drawn on top on wallpaper.
     Various foreground colors are overridden to be workspaceTextColor so that they are properly
@@ -136,7 +144,7 @@
         <item name="android:layout_gravity">center</item>
         <item name="android:focusable">true</item>
         <item name="android:gravity">center_horizontal</item>
-        <item name="android:singleLine">true</item>
+        <item name="android:lines">1</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:fontFamily">sans-serif-condensed</item>
         <item name="android:defaultFocusHighlightEnabled">false</item>
diff --git a/res/xml/flag_preferences.xml b/res/xml/flag_preferences.xml
deleted file mode 100644
index aea1a6a..0000000
--- a/res/xml/flag_preferences.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-    android:key="feature_flags"
-    android:persistent="false">
-
-</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index c55cc49..2c86f8e 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -54,9 +54,9 @@
         android:persistent="false" />
 
     <androidx.preference.PreferenceScreen
-        android:fragment="com.android.launcher3.config.FlagTogglerPreferenceFragment"
-        android:key="flag_toggler"
+        android:key="pref_developer_options"
         android:persistent="false"
-        android:title="Feature flags"/>
+        android:title="Developer Options"
+        android:fragment="com.android.launcher3.settings.DeveloperOptionsFragment"/>
 
 </androidx.preference.PreferenceScreen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 4575132..7cab18d 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -219,6 +219,17 @@
         closeAllOpenViews(activity, true);
     }
 
+    public static void closeAllOpenViewsExcept(ActivityContext activity, boolean animate,
+                                               @FloatingViewType int type) {
+        closeOpenViews(activity, animate, TYPE_ALL & ~type);
+        activity.finishAutoCancelActionMode();
+    }
+
+    public static void closeAllOpenViewsExcept(ActivityContext activity,
+                                               @FloatingViewType int type) {
+        closeAllOpenViewsExcept(activity, true, type);
+    }
+
     public static AbstractFloatingView getTopOpenView(ActivityContext activity) {
         return getTopOpenViewWithType(activity, TYPE_ALL);
     }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index f3f2238..0b2f4d9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -23,6 +25,7 @@
 import android.content.res.XmlResourceParser;
 import android.graphics.Point;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Xml;
 import android.view.Display;
 import android.view.WindowManager;
@@ -45,13 +48,13 @@
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE =
-            new MainThreadInitializedObject<>((c) -> {
-                new ConfigMonitor(c).register();
-                return new InvariantDeviceProfile(c);
-            });
+            new MainThreadInitializedObject<>(InvariantDeviceProfile::new);
 
     private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
 
+    public static final int CHANGE_FLAG_GRID = 1 << 0;
+    public static final int CHANGE_FLAG_ICON_SIZE = 1 << 1;
+
     // Constants that affects the interpolation curve between statically defined device profile
     // buckets.
     private static float KNEARESTNEIGHBOR = 3;
@@ -61,9 +64,9 @@
     private static float WEIGHT_EFFICIENT = 100000f;
 
     // Profile-defining invariant properties
-    String name;
-    float minWidthDps;
-    float minHeightDps;
+    private String name;
+    private float minWidthDps;
+    private float minHeightDps;
 
     /**
      * Number of icons per row and column in the workspace.
@@ -95,9 +98,11 @@
 
     public Point defaultWallpaperSize;
 
+    private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
+    private ConfigMonitor mConfigMonitor;
+
     @VisibleForTesting
-    public InvariantDeviceProfile() {
-    }
+    public InvariantDeviceProfile() {}
 
     private InvariantDeviceProfile(InvariantDeviceProfile p) {
         this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
@@ -125,6 +130,12 @@
 
     @TargetApi(23)
     private InvariantDeviceProfile(Context context) {
+        initGrid(context);
+        mConfigMonitor = new ConfigMonitor(context,
+                APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+    }
+
+    private void initGrid(Context context) {
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
         DisplayMetrics dm = new DisplayMetrics();
@@ -185,6 +196,44 @@
         }
     }
 
+    public void addOnChangeListener(OnIDPChangeListener listener) {
+        mChangeListeners.add(listener);
+    }
+
+    private void killProcess(Context context) {
+        Log.e("ConfigMonitor", "restarting launcher");
+        android.os.Process.killProcess(android.os.Process.myPid());
+    }
+
+    private void onConfigChanged(Context context) {
+        // Config changes, what shall we do?
+        InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
+
+        // Re-init grid
+        initGrid(context);
+
+        int changeFlags = 0;
+        if (numRows != oldProfile.numRows ||
+                numColumns != oldProfile.numColumns ||
+                numFolderColumns != oldProfile.numFolderColumns ||
+                numFolderRows != oldProfile.numFolderRows ||
+                numHotseatIcons != oldProfile.numHotseatIcons) {
+            changeFlags |= CHANGE_FLAG_GRID;
+        }
+
+        if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize) {
+            changeFlags |= CHANGE_FLAG_ICON_SIZE;
+        }
+
+        // Create a new config monitor
+        mConfigMonitor.unregister();
+        mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
+
+        for (OnIDPChangeListener listener : mChangeListeners) {
+            listener.onIdpChanged(changeFlags, this);
+        }
+    }
+
     ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
         ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
@@ -356,4 +405,8 @@
         return x * aspectRatio + y;
     }
 
+    public interface OnIDPChangeListener {
+
+        void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c50819a..3ae9a49 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -110,7 +110,6 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -137,6 +136,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -2162,11 +2162,11 @@
     }
 
     /**
-     * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
+     * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary
      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
      */
     @Override
-    public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+    public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 6bf5812..338c20b 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_SIZE;
 
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
@@ -30,6 +31,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
@@ -94,6 +96,7 @@
 
         mContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(mContext).enableAndResetCache();
+        mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
 
         if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
             mNotificationBadgingObserver = null;
@@ -113,6 +116,19 @@
         }
     }
 
+    private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
+        if (changeFlags == 0) {
+            return;
+        }
+
+        if ((changeFlags & CHANGE_FLAG_ICON_SIZE) != 0) {
+            LauncherIcons.clearPool();
+            mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
+        }
+
+        mModel.forceReload();
+    }
+
     /**
      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
      */
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 8e9021f..b3dabae 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -57,7 +57,6 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Provider;
@@ -69,6 +68,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -161,7 +161,7 @@
         public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
         public void onPageBoundSynchronously(int page);
         public void executeOnNextDraw(ViewOnDrawExecutor executor);
-        public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
+        public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
     }
 
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index dc60c8f..64b5652 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.provider.Settings;
 
 import androidx.annotation.GuardedBy;
 import androidx.annotation.Keep;
@@ -51,8 +52,10 @@
         throw new UnsupportedOperationException("Don't instantiate BaseFlags");
     }
 
-    public static boolean showFlagTogglerUi() {
-        return Utilities.IS_DEBUG_DEVICE;
+    public static boolean showFlagTogglerUi(Context context) {
+        return Utilities.IS_DEBUG_DEVICE &&
+                Settings.Global.getInt(context.getApplicationContext().getContentResolver(),
+                        Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
     }
 
     public static final boolean IS_DOGFOOD_BUILD = false;
@@ -84,9 +87,15 @@
     // trying to make them fit the orientation the device is in.
     public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
 
+    /**
+     * Feature flag to handle define config changes dynamically instead of killing the process.
+     */
+    public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
+            "APPLY_CONFIG_AT_RUNTIME", false, "Apply display changes dynamically");
+
     public static void initialize(Context context) {
-        // Avoid the disk read for builds without the flags UI.
-        if (showFlagTogglerUi()) {
+        // Avoid the disk read for user builds
+        if (Utilities.IS_DEBUG_DEVICE) {
             SharedPreferences sharedPreferences =
                     context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE);
             synchronized (sLock) {
@@ -188,5 +197,4 @@
             return h$;
         }
     }
-
 }
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
new file mode 100644
index 0000000..d3be51d
--- /dev/null
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.config;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Process;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.launcher3.R;
+import com.android.launcher3.config.BaseFlags.TogglableFlag;
+
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.SwitchPreference;
+
+/**
+ * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
+ */
+public final class FlagTogglerPrefUi {
+
+    private static final String TAG = "FlagTogglerPrefFrag";
+
+    private final PreferenceFragment mFragment;
+    private final Context mContext;
+    private final SharedPreferences mSharedPreferences;
+
+    private final PreferenceDataStore mDataStore = new PreferenceDataStore() {
+
+        @Override
+        public void putBoolean(String key, boolean value) {
+            for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+                if (flag.getKey().equals(key)) {
+                    if (value == flag.getDefaultValue()) {
+                        mSharedPreferences.edit().remove(key).apply();
+                    } else {
+                        mSharedPreferences.edit().putBoolean(key, value).apply();
+                    }
+                    updateMenu();
+                }
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String key, boolean defValue) {
+            return mSharedPreferences.getBoolean(key, defValue);
+        }
+    };
+
+    public FlagTogglerPrefUi(PreferenceFragment fragment) {
+        mFragment = fragment;
+        mContext = fragment.getActivity();
+        mSharedPreferences = mContext.getSharedPreferences(
+                FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+    }
+
+    public void applyTo(PreferenceGroup parent) {
+        // For flag overrides we only want to store when the engineer chose to override the
+        // flag with a different value than the default. That way, when we flip flags in
+        // future, engineers will pick up the new value immediately. To accomplish this, we use a
+        // custom preference data store.
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            SwitchPreference switchPreference = new SwitchPreference(mContext);
+            switchPreference.setKey(flag.getKey());
+            switchPreference.setDefaultValue(flag.getDefaultValue());
+            switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
+            switchPreference.setTitle(flag.getKey());
+            switchPreference.setSummaryOn(flag.getDefaultValue() ? "" : "overridden");
+            switchPreference.setSummaryOff(flag.getDefaultValue() ? "overridden" : "");
+            switchPreference.setPreferenceDataStore(mDataStore);
+            parent.addPreference(switchPreference);
+        }
+        updateMenu();
+    }
+
+    private void updateMenu() {
+        mFragment.setHasOptionsMenu(anyChanged());
+        mFragment.getActivity().invalidateOptionsMenu();
+    }
+
+    public void onCreateOptionsMenu(Menu menu) {
+        if (anyChanged()) {
+            menu.add(0, R.id.menu_apply_flags, 0, "Apply")
+                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        }
+    }
+
+    public void onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_apply_flags) {
+            mSharedPreferences.edit().commit();
+            Log.e(TAG,
+                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
+            System.exit(0);
+        }
+    }
+
+    public void onStop() {
+        if (anyChanged()) {
+            Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
+        return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
+    }
+
+    private boolean anyChanged() {
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java b/src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java
deleted file mode 100644
index 0a1fd2f..0000000
--- a/src/com/android/launcher3/config/FlagTogglerPreferenceFragment.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.config;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.Process;
-import android.preference.PreferenceDataStore;
-import android.preference.PreferenceFragment;
-import android.preference.SwitchPreference;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.android.launcher3.R;
-import com.android.launcher3.config.BaseFlags.TogglableFlag;
-
-/**
- * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
- */
-public final class FlagTogglerPreferenceFragment extends PreferenceFragment {
-    private static final String TAG = "FlagTogglerPrefFrag";
-
-    private SharedPreferences mSharedPreferences;
-    private MenuItem saveButton;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        addPreferencesFromResource(R.xml.flag_preferences);
-        mSharedPreferences = getContext().getSharedPreferences(
-                FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
-
-        // For flag overrides we only want to store when the engineer chose to override the
-        // flag with a different value than the default. That way, when we flip flags in
-        // future, engineers will pick up the new value immediately. To accomplish this, we use a
-        // custom preference data store.
-        getPreferenceManager().setPreferenceDataStore(new PreferenceDataStore() {
-            @Override
-            public void putBoolean(String key, boolean value) {
-                for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
-                    if (flag.getKey().equals(key)) {
-                        if (value == flag.getDefaultValue()) {
-                            mSharedPreferences.edit().remove(key).apply();
-                        } else {
-                            mSharedPreferences.edit().putBoolean(key, value).apply();
-                        }
-                    }
-                }
-            }
-        });
-
-        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
-            SwitchPreference switchPreference = new SwitchPreference(getContext());
-            switchPreference.setKey(flag.getKey());
-            switchPreference.setDefaultValue(flag.getDefaultValue());
-            switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
-            switchPreference.setTitle(flag.getKey());
-            switchPreference.setSummaryOn(flag.getDefaultValue() ? "" : "overridden");
-            switchPreference.setSummaryOff(flag.getDefaultValue() ? "overridden" : "");
-            getPreferenceScreen().addPreference(switchPreference);
-        }
-        setHasOptionsMenu(true);
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        saveButton = menu.add("Apply");
-        saveButton.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item == saveButton) {
-            mSharedPreferences.edit().commit();
-            Log.e(TAG,
-                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
-            System.exit(0);
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public void onStop() {
-        boolean anyChanged = false;
-        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
-            anyChanged = anyChanged ||
-                    getFlagStateFromSharedPrefs(flag) != flag.get();
-        }
-
-        if (anyChanged) {
-            Toast.makeText(
-                    getContext(),
-                    "Flag won't be applied until you restart launcher",
-                    Toast.LENGTH_LONG).show();
-        }
-        super.onStop();
-    }
-
-    private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
-        return mSharedPreferences.getBoolean(flag.getKey(), flag.getDefaultValue());
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index b6a8b50..8f223a3 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -232,7 +232,7 @@
                             nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
                         }
                         Utilities.scaleRectAboutCenter(bounds,
-                                li.getNormalizer().getScale(nDr, null, null, null));
+                                li.getNormalizer().getScale(nDr, null));
                     }
                     AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
 
diff --git a/src/com/android/launcher3/icons/BaseIconCache.java b/src/com/android/launcher3/icons/BaseIconCache.java
index 9198c24..2afe713 100644
--- a/src/com/android/launcher3/icons/BaseIconCache.java
+++ b/src/com/android/launcher3/icons/BaseIconCache.java
@@ -90,11 +90,11 @@
     private final HashMap<ComponentKey, CacheEntry> mCache =
             new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
     private final InstantAppResolver mInstantAppResolver;
-    final int mIconDpi;
-
-    final IconDB mIconDb;
     final Handler mWorkerHandler;
 
+    int mIconDpi;
+    IconDB mIconDb;
+
     private final BitmapFactory.Options mDecodeOptions;
 
     public BaseIconCache(Context context, int iconDpi, int iconPixelSize) {
@@ -103,8 +103,6 @@
         mUserManager = UserManagerCompat.getInstance(mContext);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconDpi = iconDpi;
-        mIconDb = new IconDB(context, iconPixelSize);
 
         mIconProvider = IconProvider.newInstance(context);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
@@ -115,6 +113,22 @@
         } else {
             mDecodeOptions = null;
         }
+
+        mIconDpi = iconDpi;
+        mIconDb = new IconDB(context, iconPixelSize);
+    }
+
+    public void updateIconParams(int iconDpi, int iconPixelSize) {
+        mWorkerHandler.post(() -> updateIconParamsBg(iconDpi, iconPixelSize));
+    }
+
+    private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) {
+        mIconDpi = iconDpi;
+        mDefaultIcons.clear();
+
+        mIconDb.close();
+        mIconDb = new IconDB(mContext, iconPixelSize);
+        mCache.clear();
     }
 
     private Drawable getFullResDefaultActivityIcon() {
diff --git a/src/com/android/launcher3/icons/BaseIconFactory.java b/src/com/android/launcher3/icons/BaseIconFactory.java
index c8c9618..cd60de5 100644
--- a/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -192,18 +192,18 @@
             }
             AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
             dr.setBounds(0, 0, 1, 1);
-            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
-            if (ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
+            scale = getNormalizer().getScale(icon, outIconBounds);
+            if (ATLEAST_OREO && !(icon instanceof AdaptiveIconDrawable)) {
                 FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
                 fsd.setDrawable(icon);
                 fsd.setScale(scale);
                 icon = dr;
-                scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+                scale = getNormalizer().getScale(icon, outIconBounds);
 
                 ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
             }
         } else {
-            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+            scale = getNormalizer().getScale(icon, outIconBounds);
         }
 
         outScale[0] = scale;
diff --git a/src/com/android/launcher3/icons/IconNormalizer.java b/src/com/android/launcher3/icons/IconNormalizer.java
index 4052a55..8eb8252 100644
--- a/src/com/android/launcher3/icons/IconNormalizer.java
+++ b/src/com/android/launcher3/icons/IconNormalizer.java
@@ -20,16 +20,13 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Matrix;
 import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
 
 import java.nio.ByteBuffer;
 
@@ -53,9 +50,6 @@
 
     private static final int MIN_VISIBLE_ALPHA = 40;
 
-    // Shape detection related constants
-    private static final float BOUND_RATIO_MARGIN = .05f;
-    private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
     private static final float SCALE_NOT_INITIALIZED = 0;
 
     // Ratio of the diameter of an normalized circular icon to the actual icon size.
@@ -64,8 +58,6 @@
     private final int mMaxSize;
     private final Bitmap mBitmap;
     private final Canvas mCanvas;
-    private final Paint mPaintMaskShape;
-    private final Paint mPaintMaskShapeOutline;
     private final byte[] mPixels;
 
     private final Rect mAdaptiveIconBounds;
@@ -75,8 +67,6 @@
     private final float[] mLeftBorder;
     private final float[] mRightBorder;
     private final Rect mBounds;
-    private final Path mShapePath;
-    private final Matrix mMatrix;
 
     /** package private **/
     IconNormalizer(Context context, int iconBitmapSize) {
@@ -90,89 +80,10 @@
         mBounds = new Rect();
         mAdaptiveIconBounds = new Rect();
 
-        mPaintMaskShape = new Paint();
-        mPaintMaskShape.setColor(Color.RED);
-        mPaintMaskShape.setStyle(Paint.Style.FILL);
-        mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
-
-        mPaintMaskShapeOutline = new Paint();
-        mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density);
-        mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
-        mPaintMaskShapeOutline.setColor(Color.BLACK);
-        mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-
-        mShapePath = new Path();
-        mMatrix = new Matrix();
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
     }
 
     /**
-     * Returns if the shape of the icon is same as the path.
-     * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
-     */
-    private boolean isShape(Path maskPath) {
-        // Condition1:
-        // If width and height of the path not close to a square, then the icon shape is
-        // not same as the mask shape.
-        float iconRatio = ((float) mBounds.width()) / mBounds.height();
-        if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
-            if (DEBUG) {
-                Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio);
-            }
-            return false;
-        }
-
-        // Condition 2:
-        // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
-        // should generate transparent image, if the actual icon is equivalent to the shape.
-
-        // Fit the shape within the icon's bounding box
-        mMatrix.reset();
-        mMatrix.setScale(mBounds.width(), mBounds.height());
-        mMatrix.postTranslate(mBounds.left, mBounds.top);
-        maskPath.transform(mMatrix, mShapePath);
-
-        // XOR operation
-        mCanvas.drawPath(mShapePath, mPaintMaskShape);
-
-        // DST_OUT operation around the mask path outline
-        mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
-
-        // Check if the result is almost transparent
-        return isTransparentBitmap();
-    }
-
-    /**
-     * Used to determine if certain the bitmap is transparent.
-     */
-    private boolean isTransparentBitmap() {
-        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
-        buffer.rewind();
-        mBitmap.copyPixelsToBuffer(buffer);
-
-        int y = mBounds.top;
-        // buffer position
-        int index = y * mMaxSize;
-        // buffer shift after every row, width of buffer = mMaxSize
-        int rowSizeDiff = mMaxSize - mBounds.right;
-
-        int sum = 0;
-        for (; y < mBounds.bottom; y++) {
-            index += mBounds.left;
-            for (int x = mBounds.left; x < mBounds.right; x++) {
-                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
-                    sum++;
-                }
-                index++;
-            }
-            index += rowSizeDiff;
-        }
-
-        float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
-        return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
-    }
-
-    /**
      * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
      * matches the design guidelines for a launcher icon.
      *
@@ -186,8 +97,7 @@
      *
      * @param outBounds optional rect to receive the fraction distance from each edge.
      */
-    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
-            @Nullable Path path, @Nullable boolean[] outMaskShape) {
+    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) {
         if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
             if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
                 if (outBounds != null) {
@@ -298,10 +208,6 @@
                     1 - ((float) mBounds.right) / width,
                     1 - ((float) mBounds.bottom) / height);
         }
-
-        if (outMaskShape != null && outMaskShape.length > 0) {
-            outMaskShape[0] = isShape(path);
-        }
         float areaScale = area / (width * height);
         // Use sqrt of the final ratio as the images is scaled across both width and height.
         float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 69614a3..c96d35d 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -27,11 +27,11 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.graphics.BitmapRenderer;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.BitmapRenderer;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -48,13 +48,14 @@
 
     private static final Object sPoolSync = new Object();
     private static LauncherIcons sPool;
-    private LauncherIcons next;
+    private static int sPoolId = 0;
 
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
     public static LauncherIcons obtain(Context context) {
+        int poolId;
         synchronized (sPoolSync) {
             if (sPool != null) {
                 LauncherIcons m = sPool;
@@ -62,9 +63,33 @@
                 m.next = null;
                 return m;
             }
+            poolId = sPoolId;
         }
+
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize);
+        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
+    }
+
+    public static void clearPool() {
+        synchronized (sPoolSync) {
+            sPool = null;
+            sPoolId++;
+        }
+    }
+
+    private final Context mContext;
+    private final int mFillResIconDpi;
+    private final int mIconBitmapSize;
+    private final int mPoolId;
+
+    private LauncherIcons next;
+
+    private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+        super(context, fillResIconDpi, iconBitmapSize);
+        mContext = context.getApplicationContext();
+        mFillResIconDpi = fillResIconDpi;
+        mIconBitmapSize = iconBitmapSize;
+        mPoolId = poolId;
     }
 
     /**
@@ -72,6 +97,9 @@
      */
     public void recycle() {
         synchronized (sPoolSync) {
+            if (sPoolId != mPoolId) {
+                return;
+            }
             // Clear any temporary state variables
             clear();
 
@@ -85,17 +113,6 @@
         recycle();
     }
 
-    private final Context mContext;
-    private final int mFillResIconDpi;
-    private final int mIconBitmapSize;
-
-    private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize) {
-        super(context, fillResIconDpi, iconBitmapSize);
-        mContext = context.getApplicationContext();
-        mFillResIconDpi = fillResIconDpi;
-        mIconBitmapSize = iconBitmapSize;
-    }
-
     public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
             int iconAppTargetSdk) {
         return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index fcdc088..c9d8e3e 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -27,10 +27,10 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.concurrent.Executor;
 
 /**
@@ -107,13 +107,9 @@
     }
 
     public void bindDeepShortcuts(BgDataModel dataModel) {
-        final MultiHashMap<ComponentKey, String> shortcutMapCopy = dataModel.deepShortcutMap.clone();
-        scheduleCallbackTask(new CallbackTask() {
-            @Override
-            public void execute(Callbacks callbacks) {
-                callbacks.bindDeepShortcutMap(shortcutMapCopy);
-            }
-        });
+        final HashMap<ComponentKey, Integer> shortcutMapCopy =
+                new HashMap<>(dataModel.deepShortcutMap);
+        scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy));
     }
 
     public void bindUpdatedWidgets(BgDataModel dataModel) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 81eefc4..151d6f4 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -38,7 +38,6 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.util.MultiHashMap;
 import com.google.protobuf.nano.MessageNano;
 
 import java.io.FileDescriptor;
@@ -97,9 +96,9 @@
     public boolean hasShortcutHostPermission;
 
     /**
-     * Maps all launcher activities to the id's of their shortcuts (if they have any).
+     * Maps all launcher activities to counts of their shortcuts.
      */
-    public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
+    public final HashMap<ComponentKey, Integer> deepShortcutMap = new HashMap<>();
 
     /**
      * Entire list of widgets.
@@ -154,14 +153,11 @@
         }
 
         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
-            writer.println(prefix + "shortcuts");
-            for (ArrayList<String> map : deepShortcutMap.values()) {
-                writer.print(prefix + "  ");
-                for (String str : map) {
-                    writer.print(str + ", ");
-                }
-                writer.println();
+            writer.println(prefix + "shortcut counts ");
+            for (Integer count : deepShortcutMap.values()) {
+                writer.print(count + ", ");
             }
+            writer.println();
         }
     }
 
@@ -359,9 +355,9 @@
     }
 
     /**
-     * Clear all the deep shortcuts for the given package, and re-add the new shortcuts.
+     * Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
      */
-    public synchronized void updateDeepShortcutMap(
+    public synchronized void updateDeepShortcutCounts(
             String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts) {
         if (packageName != null) {
             Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
@@ -381,7 +377,9 @@
             if (shouldShowInContainer) {
                 ComponentKey targetComponent
                         = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
-                deepShortcutMap.addToList(targetComponent, shortcut.getId());
+
+                Integer previousCount = deepShortcutMap.get(targetComponent);
+                deepShortcutMap.put(targetComponent, previousCount == null ? 1 : previousCount + 1);
             }
         }
     }
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 2c15df1..1d18e76 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
@@ -42,9 +41,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -333,20 +331,16 @@
     }
 
     public void bindDeepShortcuts() {
-        final MultiHashMap<ComponentKey, String> shortcutMapCopy;
+        final HashMap<ComponentKey, Integer> shortcutMapCopy;
         synchronized (mBgDataModel) {
-            shortcutMapCopy = mBgDataModel.deepShortcutMap.clone();
+            shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
         }
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.bindDeepShortcutMap(shortcutMapCopy);
-                }
+        mUiExecutor.execute(() -> {
+            Callbacks callbacks = mCallbacks.get();
+            if (callbacks != null) {
+                callbacks.bindDeepShortcutMap(shortcutMapCopy);
             }
-        };
-        mUiExecutor.execute(r);
+        });
     }
 
     public void bindAllApps() {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 405125e..8b3e2c9 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -861,7 +861,7 @@
                 if (mUserManager.isUserUnlocked(user)) {
                     List<ShortcutInfoCompat> shortcuts =
                             mShortcutManager.queryForAllShortcuts(user);
-                    mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+                    mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
                 }
             }
         }
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 47fcd9e..e99fed9 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -116,7 +116,7 @@
 
         if (mUpdateIdMap) {
             // Update the deep shortcut map if the list of ids has changed for an activity.
-            dataModel.updateDeepShortcutMap(mPackageName, mUser, mShortcuts);
+            dataModel.updateDeepShortcutCounts(mPackageName, mUser, mShortcuts);
             bindDeepShortcuts(dataModel);
         }
     }
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 40c1912..8e7557a 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -117,7 +117,7 @@
         }
 
         if (isUserUnlocked) {
-            dataModel.updateDeepShortcutMap(
+            dataModel.updateDeepShortcutCounts(
                     null, mUser, deepShortcutManager.queryForAllShortcuts(mUser));
         }
         bindDeepShortcuts(dataModel);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b9e6a98..12319f7 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -216,13 +216,17 @@
             BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
         PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider();
         populateAndShow(icon,
-                popupDataProvider.getShortcutIdsForItem(item),
+                popupDataProvider.getShortcutCountForItem(item),
                 popupDataProvider.getNotificationKeysForItem(item),
                 factory.getEnabledShortcuts(mLauncher, item));
     }
 
+    public ViewGroup getSystemShortcutContainerForTesting() {
+        return mSystemShortcutContainer;
+    }
+
     @TargetApi(Build.VERSION_CODES.P)
-    protected void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
+    protected void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
             final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
         mNumNotifications = notificationKeys.size();
         mOriginalIcon = originalIcon;
@@ -240,12 +244,12 @@
         int viewsToFlip = getChildCount();
         mSystemShortcutContainer = this;
 
-        if (!shortcutIds.isEmpty()) {
+        if (shortcutCount > 0) {
             if (mNotificationItemView != null) {
                 mNotificationItemView.addGutter();
             }
 
-            for (int i = shortcutIds.size(); i > 0; i--) {
+            for (int i = shortcutCount; i > 0; i--) {
                 mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut, this));
             }
             updateHiddenShortcuts();
@@ -284,7 +288,7 @@
         final Looper workerLooper = LauncherModel.getWorkerLooper();
         new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
                 mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
-                this, shortcutIds, mShortcuts, notificationKeys));
+                this, mShortcuts, notificationKeys));
     }
 
     private String getTitleForAccessibility() {
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 4d5a9c6..3206503 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -29,7 +29,6 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
@@ -52,8 +51,8 @@
 
     private final Launcher mLauncher;
 
-    /** Maps launcher activity components to their list of shortcut ids. */
-    private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
+    /** Maps launcher activity components to a count of how many shortcuts they have. */
+    private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
     /** Maps packages to their BadgeInfo's . */
     private Map<PackageUserKey, BadgeInfo> mPackageUserToBadgeInfos = new HashMap<>();
     /** Maps packages to their Widgets */
@@ -146,22 +145,22 @@
         }
     }
 
-    public void setDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+    public void setDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
         mDeepShortcutMap = deepShortcutMapCopy;
         if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
     }
 
-    public List<String> getShortcutIdsForItem(ItemInfo info) {
+    public int getShortcutCountForItem(ItemInfo info) {
         if (!DeepShortcutManager.supportsShortcuts(info)) {
-            return Collections.EMPTY_LIST;
+            return 0;
         }
         ComponentName component = info.getTargetComponent();
         if (component == null) {
-            return Collections.EMPTY_LIST;
+            return 0;
         }
 
-        List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
-        return ids == null ? Collections.EMPTY_LIST : ids;
+        Integer count = mDeepShortcutMap.get(new ComponentKey(component, info.user));
+        return count == null ? 0 : count;
     }
 
     public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 61113b8..2c59202 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -124,7 +124,7 @@
 
     public static Runnable createUpdateRunnable(final Launcher launcher, final ItemInfo originalInfo,
             final Handler uiHandler, final PopupContainerWithArrow container,
-            final List<String> shortcutIds, final List<DeepShortcutView> shortcutViews,
+            final List<DeepShortcutView> shortcutViews,
             final List<NotificationKeyData> notificationKeys) {
         final ComponentName activity = originalInfo.getTargetComponent();
         final UserHandle user = originalInfo.user;
@@ -141,7 +141,7 @@
             }
 
             List<ShortcutInfoCompat> shortcuts = DeepShortcutManager.getInstance(launcher)
-                    .queryForShortcutsContainer(activity, shortcutIds, user);
+                    .queryForShortcutsContainer(activity, user);
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index af0d3da..c76fb96 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.RemoteAction;
+import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -48,14 +49,20 @@
             AbstractFloatingView.closeAllOpenViews(launcher);
 
             try {
-                mAction.getActionIntent().send(0,
+                mAction.getActionIntent().send(
+                        launcher,
+                        0,
+                        new Intent().putExtra(
+                                Intent.EXTRA_PACKAGE_NAME,
+                                itemInfo.getTargetComponent().getPackageName()),
                         (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
                             if (resultData != null && !resultData.isEmpty()) {
                                 Log.e(TAG, "Remote action returned result: " + mAction.getTitle()
                                         + " : " + resultData);
                                 Toast.makeText(launcher, resultData, Toast.LENGTH_SHORT).show();
                             }
-                        }, new Handler(Looper.getMainLooper()));
+                        },
+                        new Handler(Looper.getMainLooper()));
             } catch (PendingIntent.CanceledException e) {
                 Log.e(TAG, "Remote action canceled: " + mAction.getTitle(), e);
                 Toast.makeText(launcher, launcher.getString(
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
new file mode 100644
index 0000000..a9242f9
--- /dev/null
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.settings;
+
+import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
+import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.FlagTogglerPrefUi;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+
+import java.util.List;
+import java.util.Set;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
+
+/**
+ * Dev-build only UI allowing developers to toggle flag settings and plugins.
+ * See {@link FeatureFlags}.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class DeveloperOptionsFragment extends PreferenceFragment {
+
+    private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
+    private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
+
+    private final BroadcastReceiver mPluginReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            loadPluginPrefs();
+        }
+    };
+
+    private PreferenceScreen mPreferenceScreen;
+
+    private PreferenceCategory mPluginsCategory;
+    private FlagTogglerPrefUi mFlagTogglerPrefUi;
+
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        getContext().registerReceiver(mPluginReceiver, filter);
+        getContext().registerReceiver(mPluginReceiver,
+                new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+
+        mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext());
+        setPreferenceScreen(mPreferenceScreen);
+
+        initFlags();
+        loadPluginPrefs();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        getContext().unregisterReceiver(mPluginReceiver);
+    }
+
+    private PreferenceCategory newCategory(String title) {
+        PreferenceCategory category = new PreferenceCategory(getContext());
+        category.setOrder(Preference.DEFAULT_ORDER);
+        category.setTitle(title);
+        mPreferenceScreen.addPreference(category);
+        return category;
+    }
+
+    private void initFlags() {
+        if (!FeatureFlags.showFlagTogglerUi(getContext())) {
+            return;
+        }
+
+        mFlagTogglerPrefUi = new FlagTogglerPrefUi(this);
+        mFlagTogglerPrefUi.applyTo(newCategory("Feature flags"));
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onCreateOptionsMenu(menu);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onOptionsItemSelected(item);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void onStop() {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onStop();
+        }
+        super.onStop();
+    }
+
+    private void loadPluginPrefs() {
+        if (mPluginsCategory != null) {
+            mPreferenceScreen.removePreference(mPluginsCategory);
+        }
+        if (!PluginManagerWrapper.hasPlugins(getActivity())) {
+            mPluginsCategory = null;
+            return;
+        }
+        mPluginsCategory = newCategory("Plugins");
+
+        PluginManagerWrapper manager = PluginManagerWrapper.INSTANCE.get(getContext());
+        Context prefContext = getContext();
+        PackageManager pm = getContext().getPackageManager();
+
+        Set<String> pluginActions = manager.getPluginActions();
+        ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>();
+        for (String action : pluginActions) {
+            String name = toName(action);
+            List<ResolveInfo> result = pm.queryIntentServices(
+                    new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS);
+            for (ResolveInfo info : result) {
+                String packageName = info.serviceInfo.packageName;
+                if (!plugins.containsKey(packageName)) {
+                    plugins.put(packageName, new ArraySet<>());
+                }
+                plugins.get(packageName).add(name);
+            }
+        }
+
+        List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{PLUGIN_PERMISSION},
+                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
+        PreferenceDataStore enabled = manager.getPluginEnabler();
+        apps.forEach(app -> {
+            if (!plugins.containsKey(app.packageName)) return;
+            SwitchPreference pref = new PluginPreference(prefContext, app, enabled);
+            pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
+            mPluginsCategory.addPreference(pref);
+        });
+    }
+
+    private String toString(ArraySet<String> plugins) {
+        StringBuilder b = new StringBuilder();
+        for (String string : plugins) {
+            if (b.length() != 0) {
+                b.append(", ");
+            }
+            b.append(string);
+        }
+        return b.toString();
+    }
+
+    private String toName(String action) {
+        String str = action.replace("com.android.systemui.action.PLUGIN_", "");
+        StringBuilder b = new StringBuilder();
+        for (String s : str.split("_")) {
+            if (b.length() != 0) {
+                b.append(' ');
+            }
+            b.append(s.substring(0, 1));
+            b.append(s.substring(1).toLowerCase());
+        }
+        return b.toString();
+    }
+
+    private static class PluginPreference extends SwitchPreference {
+        private final boolean mHasSettings;
+        private final PackageInfo mInfo;
+        private final PreferenceDataStore mPluginEnabler;
+
+        public PluginPreference(Context prefContext, PackageInfo info,
+                PreferenceDataStore pluginEnabler) {
+            super(prefContext);
+            PackageManager pm = prefContext.getPackageManager();
+            mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
+                    .setPackage(info.packageName), 0) != null;
+            mInfo = info;
+            mPluginEnabler = pluginEnabler;
+            setTitle(info.applicationInfo.loadLabel(pm));
+            setChecked(isPluginEnabled());
+            setWidgetLayoutResource(R.layout.switch_preference_with_settings);
+        }
+
+        private boolean isEnabled(ComponentName cn) {
+            return mPluginEnabler.getBoolean(pluginEnabledKey(cn), true);
+
+        }
+
+        private boolean isPluginEnabled() {
+            for (int i = 0; i < mInfo.services.length; i++) {
+                ComponentName componentName = new ComponentName(mInfo.packageName,
+                        mInfo.services[i].name);
+                if (!isEnabled(componentName)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        protected boolean persistBoolean(boolean isEnabled) {
+            boolean shouldSendBroadcast = false;
+            for (int i = 0; i < mInfo.services.length; i++) {
+                ComponentName componentName = new ComponentName(mInfo.packageName,
+                        mInfo.services[i].name);
+
+                if (isEnabled(componentName) != isEnabled) {
+                    mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled);
+                    shouldSendBroadcast = true;
+                }
+            }
+            if (shouldSendBroadcast) {
+                final String pkg = mInfo.packageName;
+                final Intent intent = new Intent(PLUGIN_CHANGED,
+                        pkg != null ? Uri.fromParts("package", pkg, null) : null);
+                getContext().sendBroadcast(intent);
+            }
+            setChecked(isEnabled);
+            return true;
+        }
+
+        @Override
+        public void onBindViewHolder(PreferenceViewHolder holder) {
+            super.onBindViewHolder(holder);
+            holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
+                    : View.GONE);
+            holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
+                    : View.GONE);
+            holder.findViewById(R.id.settings).setOnClickListener(v -> {
+                ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
+                        new Intent(ACTION_PLUGIN_SETTINGS).setPackage(
+                                mInfo.packageName), 0);
+                if (result != null) {
+                    v.getContext().startActivity(new Intent().setComponent(
+                            new ComponentName(result.activityInfo.packageName,
+                                    result.activityInfo.name)));
+                }
+            });
+            holder.itemView.setOnLongClickListener(v -> {
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                intent.setData(Uri.fromParts("package", mInfo.packageName, null));
+                getContext().startActivity(intent);
+                return true;
+            });
+        }
+    }
+}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 66420d0..7c158d9 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -24,17 +24,16 @@
 import android.app.Activity;
 import android.app.DialogFragment;
 import android.app.Fragment;
-import android.content.Intent;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.launcher3.LauncherFiles;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.IconShapeOverride;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.SecureSettingsObserver;
 
 import androidx.preference.ListPreference;
@@ -52,6 +51,7 @@
 public class SettingsActivity extends Activity
         implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback {
 
+    private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
     private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
 
     private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
@@ -203,7 +203,12 @@
 
                 case FLAGS_PREFERENCE_KEY:
                     // Only show flag toggler UI if this build variant implements that.
-                    return FeatureFlags.showFlagTogglerUi();
+                    return FeatureFlags.showFlagTogglerUi(getContext());
+
+                case DEVELOPER_OPTIONS_KEY:
+                    // Show if plugins are enabled or flag UI is enabled.
+                    return FeatureFlags.showFlagTogglerUi(getContext()) ||
+                            PluginManagerWrapper.hasPlugins(getContext());
             }
 
             return true;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 24e2e2f..e70aac6 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -94,10 +94,11 @@
      * Gets all the manifest and dynamic shortcuts associated with the given package and user,
      * to be displayed in the shortcuts container on long press.
      */
+    @TargetApi(25)
     public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
-            List<String> ids, UserHandle user) {
+            UserHandle user) {
         return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
-                activity.getPackageName(), activity, ids, user);
+                activity.getPackageName(), activity, null, user);
     }
 
     /**
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index c809f27..2daa2fe 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -18,8 +18,10 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.widget.Toast;
@@ -38,6 +40,10 @@
 
     private Toast mInstructionToast;
 
+    private boolean mShowLoadingState;
+    private Drawable mLoadingStatePlaceholder;
+    private final Rect mLoadingStateBounds = new Rect();
+
     public DeepShortcutTextView(Context context) {
         this(context, null, 0);
     }
@@ -53,6 +59,7 @@
         mDragHandleWidth = resources.getDimensionPixelSize(R.dimen.popup_padding_end)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drag_handle_size)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drawable_padding) / 2;
+        showLoadingState(true);
     }
 
     @Override
@@ -63,6 +70,25 @@
         if (!Utilities.isRtl(getResources())) {
             mDragHandleBounds.offset(getMeasuredWidth() - mDragHandleBounds.width(), 0);
         }
+
+        setLoadingBounds();
+    }
+
+    private void setLoadingBounds() {
+        if (mLoadingStatePlaceholder == null) {
+            return;
+        }
+        mLoadingStateBounds.set(
+                0,
+                0,
+                getMeasuredWidth() - mDragHandleWidth - getPaddingStart(),
+                mLoadingStatePlaceholder.getIntrinsicHeight());
+        mLoadingStateBounds.offset(
+                Utilities.isRtl(getResources()) ? mDragHandleWidth : getPaddingStart(),
+                (int) ((getMeasuredHeight() - mLoadingStatePlaceholder.getIntrinsicHeight())
+                        / 2.0f)
+        );
+        mLoadingStatePlaceholder.setBounds(mLoadingStateBounds);
     }
 
     @Override
@@ -71,6 +97,15 @@
     }
 
     @Override
+    public void setText(CharSequence text, BufferType type) {
+        super.setText(text, type);
+
+        if (!TextUtils.isEmpty(text)) {
+            showLoadingState(false);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             // Show toast if user touches the drag handle (long clicks still start the drag).
@@ -88,6 +123,34 @@
         return super.performClick();
     }
 
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (!mShowLoadingState) {
+            super.onDraw(canvas);
+            return;
+        }
+
+        mLoadingStatePlaceholder.draw(canvas);
+    }
+
+    private void showLoadingState(boolean loading) {
+        if (loading == mShowLoadingState) {
+            return;
+        }
+
+        mShowLoadingState = loading;
+
+        if (loading) {
+            mLoadingStatePlaceholder = getContext().getDrawable(
+                    R.drawable.deep_shortcuts_text_placeholder);
+            setLoadingBounds();
+        } else {
+            mLoadingStatePlaceholder = null;
+        }
+
+        invalidate();
+    }
+
     private void showToast() {
         if (mInstructionToast != null) {
             mInstructionToast.cancel();
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index c856cdb..7b93ba2 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -40,8 +40,6 @@
 
     private static final Point sTempPoint = new Point();
 
-    private final Rect mPillRect;
-
     private BubbleTextView mBubbleText;
     private View mIconView;
     private View mDivider;
@@ -59,8 +57,6 @@
 
     public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
-        mPillRect = new Rect();
     }
 
     @Override
@@ -98,12 +94,6 @@
         return sTempPoint;
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
-    }
-
     /** package private **/
     public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
             PopupContainerWithArrow container) {
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 5dd0d08..717acdc 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -29,9 +29,12 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities.Consumer;
+
 /**
  * {@link BroadcastReceiver} which watches configuration changes and
- * restarts the process in case changes which affect the device profile occur.
+ * notifies the callback in case changes which affect the device profile occur.
  */
 public class ConfigMonitor extends BroadcastReceiver implements DisplayListener {
 
@@ -48,7 +51,9 @@
     private final Point mRealSize;
     private final Point mSmallestSize, mLargestSize;
 
-    public ConfigMonitor(Context context) {
+    private Consumer<Context> mCallback;
+
+    public ConfigMonitor(Context context, Consumer<Context> callback) {
         mContext = context;
 
         Configuration config = context.getResources().getConfiguration();
@@ -64,6 +69,12 @@
         mSmallestSize = new Point();
         mLargestSize = new Point();
         display.getCurrentSizeRange(mSmallestSize, mLargestSize);
+
+        mCallback = callback;
+
+        mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+        mContext.getSystemService(DisplayManager.class)
+                .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
     }
 
     @Override
@@ -71,16 +82,10 @@
         Configuration config = context.getResources().getConfiguration();
         if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
             Log.d(TAG, "Configuration changed");
-            killProcess();
+            notifyChange();
         }
     }
 
-    public void register() {
-        mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
-        mContext.getSystemService(DisplayManager.class)
-                .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
-    }
-
     @Override
     public void onDisplayAdded(int displayId) { }
 
@@ -97,7 +102,7 @@
 
         if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
             Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
-            killProcess();
+            notifyChange();
             return;
         }
 
@@ -105,22 +110,28 @@
         if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) {
             Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
                     mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2));
-            killProcess();
+            notifyChange();
         }
     }
 
-    private void killProcess() {
-        Log.d(TAG, "restarting launcher");
-        try {
-            mContext.unregisterReceiver(this);
-            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
-        } catch (Exception e) {
-            // We are going to die anyway, ignore any error die to race condition in registering.
+    private synchronized void notifyChange() {
+        if (mCallback != null) {
+            Consumer<Context> callback = mCallback;
+            mCallback = null;
+            new MainThreadExecutor().execute(() -> callback.accept(mContext));
         }
-        android.os.Process.killProcess(android.os.Process.myPid());
     }
 
     private Display getDefaultDisplay(Context context) {
         return context.getSystemService(WindowManager.class).getDefaultDisplay();
     }
+
+    public void unregister() {
+        try {
+            mContext.unregisterReceiver(this);
+            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to unregister config monitor", e);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index 44c1762..3faf070 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -87,6 +87,10 @@
         mOpenHelper.clearDB(mOpenHelper.getWritableDatabase());
     }
 
+    public void close() {
+        mOpenHelper.close();
+    }
+
     protected abstract void onCreateTable(SQLiteDatabase db);
 
     /**
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 3e58ea6..c540b59 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -136,7 +136,7 @@
     }
 
     @VisibleForTesting
-    public static OptionsPopupView getOptionsPopup(Launcher launcher) {
+    public static ArrowPopup getOptionsPopup(Launcher launcher) {
         return launcher.findViewById(R.id.deep_shortcuts_container);
     }
 
diff --git a/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java b/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java
new file mode 100644
index 0000000..8d7dd4b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java
@@ -0,0 +1,15 @@
+package com.android.systemui.plugins;
+
+import android.view.ViewGroup;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to wrap the widget on the first home screen, e.g. to add new content.
+ */
+@ProvidesInterface(action = FirstScreenWidget.ACTION, version = FirstScreenWidget.VERSION)
+public interface FirstScreenWidget extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_FIRST_SCREEN_WIDGET";
+    int VERSION = 1;
+
+    void onWidgetUpdated(ViewGroup widgetView);
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 31dbb34..e1a35c9 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -14,17 +14,26 @@
 
 package com.android.launcher3.uioverrides.plugins;
 
+import android.content.ComponentName;
 import android.content.Context;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 
+import java.util.Collections;
+import java.util.Set;
+
+import androidx.preference.PreferenceDataStore;
+
 public class PluginManagerWrapper {
 
     public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
             new MainThreadInitializedObject<>(PluginManagerWrapper::new);
 
+    private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
+    public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
+
     private PluginManagerWrapper(Context c) {
     }
 
@@ -35,6 +44,21 @@
             boolean allowMultiple) {
     }
 
-    public void removePluginListener(PluginListener<? extends Plugin> listener) {
+    public void removePluginListener(PluginListener<? extends Plugin> listener) { }
+
+    public Set<String> getPluginActions() {
+        return Collections.emptySet();
+    }
+
+    public PreferenceDataStore getPluginEnabler() {
+        return new PreferenceDataStore() { };
+    }
+
+    public static String pluginEnabledKey(ComponentName cn) {
+        return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
+    }
+
+    public static boolean hasPlugins(Context context) {
+        return false;
     }
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index d808873..a787537 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,7 +21,8 @@
 include $(CLEAR_VARS)
 LOCAL_STATIC_JAVA_LIBRARIES := \
 	androidx.annotation_annotation \
-	androidx-test \
+	androidx.test.runner \
+	androidx.test.rules \
 	androidx.test.uiautomator_uiautomator \
 	libSharedSystemUI
 
@@ -42,7 +43,8 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
-	androidx-test \
+	androidx.test.runner \
+	androidx.test.rules \
 	androidx.test.uiautomator_uiautomator \
 	mockito-target-minus-junit4
 
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index c878699..532d3e8 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -56,6 +56,7 @@
 import org.junit.rules.TestRule;
 import org.junit.runners.model.Statement;
 
+import java.io.IOException;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -168,6 +169,16 @@
         }
     }
 
+    protected void clearLauncherData() throws IOException {
+        if (TestHelpers.isInLauncherProcess()) {
+            LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+            resetLoaderState();
+        } else {
+            mDevice.executeShellCommand("pm clear " + mDevice.getLauncherPackageName());
+        }
+    }
+
     /**
      * Scrolls the {@param container} until it finds an object matching {@param condition}.
      * @return the matching object.
@@ -261,6 +272,10 @@
                 launcher -> launcher.getStateManager().getState() == state);
     }
 
+    protected void waitForResumed(String message) {
+        waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
+    }
+
     // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
     // flakiness.
     protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index 5244386..6fa28f1 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -28,6 +28,8 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
@@ -42,6 +44,7 @@
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
 
 import java.util.concurrent.Callable;
+import java.util.function.Function;
 
 public class TestViewHelpers {
     private static final String TAG = "TestViewHelpers";
@@ -183,4 +186,12 @@
                 AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT).click();
         return findViewById(R.id.widgets_list_view);
     }
+
+    public static View findChildView(ViewGroup parent, Function<View, Boolean> condition) {
+        for (int i = 0; i < parent.getChildCount(); ++i) {
+            final View child = parent.getChildAt(i);
+            if (condition.apply(child)) return child;
+        }
+        return null;
+    }
 }