Merge "Revert "Calling start/stop listening in onStart/onStop"" into ub-launcher3-calgary
diff --git a/Android.mk b/Android.mk
index e42a463..b080da2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,6 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    libWallpaperPicker \
     android-support-v4 \
     android-support-v7-recyclerview \
     android-support-v7-palette
@@ -32,9 +31,9 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     $(call all-proto-files-under, protos)
 
-LOCAL_RESOURCE_DIR := packages/apps/WallpaperPicker/res \
+LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
-    prebuilts/sdk/current/support/v7/recyclerview/res
+    frameworks/support/v7/recyclerview/res
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
@@ -43,7 +42,6 @@
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
     --extra-packages android.support.v7.recyclerview \
-    --extra-packages com.android.wallpaperpicker
 
 LOCAL_SDK_VERSION := current
 LOCAL_PACKAGE_NAME := Launcher3
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 80ffa43..9ae311e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -40,13 +40,6 @@
         android:protectionLevel="signatureOrSystem"
         android:label="@string/permlab_write_settings"
         android:description="@string/permdesc_write_settings"/>
-    <permission
-        android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"
-        android:protectionLevel="signature"
-        />
-    <permission
-        android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST"
-        android:protectionLevel="signatureOrSystem" />
 
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
@@ -58,8 +51,6 @@
     <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
-    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST" />
 
     <application
         android:allowBackup="@bool/enable_backup"
@@ -92,33 +83,6 @@
         </activity>
 
         <activity
-            android:name="com.android.launcher3.WallpaperPickerActivity"
-            android:theme="@style/WallpaperTheme.Picker"
-            android:label="@string/pick_wallpaper"
-            android:icon="@mipmap/ic_launcher_wallpaper"
-            android:finishOnCloseSystemDialogs="true"
-            android:process=":wallpaper_chooser">
-            <intent-filter>
-                <action android:name="android.intent.action.SET_WALLPAPER" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name="com.android.wallpaperpicker.WallpaperCropActivity"
-            android:theme="@style/WallpaperTheme"
-            android:label="@string/crop_wallpaper"
-            android:icon="@mipmap/ic_launcher_wallpaper"
-            android:finishOnCloseSystemDialogs="true"
-            android:process=":wallpaper_chooser">
-            <intent-filter>
-                <action android:name="android.service.wallpaper.CROP_AND_SET_WALLPAPER" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="image/*" />
-            </intent-filter>
-        </activity>
-
-        <activity
             android:name="com.android.launcher3.SettingsActivity"
             android:label="@string/settings_button_text"
             android:autoRemoveFromRecents="true"
diff --git a/build.gradle b/build.gradle
index 2306744..4df4063 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,7 +3,7 @@
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:+'
+        classpath 'com.android.tools.build:gradle:1.5.0'
         classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
     }
 }
@@ -54,7 +54,6 @@
     compile 'com.android.support:recyclerview-v7:23.1.1'
     compile 'com.android.support:palette-v7:23.2.0'
     compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
-    compile project(":WallpaperPicker-Lib")
 
     testCompile 'junit:junit:4.12'
     androidTestCompile 'com.android.support.test:runner:0.5'
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index a7b6429..eae02ca 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -32,59 +32,66 @@
 
   // For container type and item type
   // Used mainly for ContainerType.FOLDER, ItemType.*
-  optional Target parent = 2;
-  optional int32 page_index = 3;
-  optional int32 rank = 4;
-  optional int32 grid_x = 5;
-  optional int32 grid_y = 6;
+  optional int32 page_index = 2;
+  optional int32 rank = 3;
+  optional int32 grid_x = 4;
+  optional int32 grid_y = 5;
 
   // For container types only
-  optional ContainerType container_type = 7;
-  optional int32 cardinality = 8;
+  optional ContainerType container_type = 6;
+  optional int32 cardinality = 7;
 
   // For control types only
-  optional ControlType control_type = 9;
+  optional ControlType control_type = 8;
 
   // For item types only
-  optional ItemType item_type = 10;
-  optional int32 package_name_hash = 11;
-  optional int32 component_hash = 12;      // Used for ItemType.WIDGET
-  optional int32 intent_hash = 13;         // Used for ItemType.SHORTCUT
-  optional int32 span_x = 14 [default = 1];// Used for ItemType.WIDGET
-  optional int32 span_y = 15 [default = 1];// Used for ItemType.WIDGET
+  optional ItemType item_type = 9;
+  optional int32 package_name_hash = 10;
+  optional int32 component_hash = 11;      // Used for ItemType.WIDGET
+  optional int32 intent_hash = 12;         // Used for ItemType.SHORTCUT
+  optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET
+  optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
+  optional int32 predictedRank = 15;
 }
 
+// Used to define what type of item a Target would represent.
 enum ItemType {
-  APP_ICON = 0;
-  SHORTCUT = 1;
-  WIDGET = 2;
-  FOLDER_ICON = 3;
+  DEFAULT_ITEMTYPE = 0;
+  APP_ICON = 1;
+  SHORTCUT = 2;
+  WIDGET = 3;
+  FOLDER_ICON = 4;
 }
 
+// Used to define what type of container a Target would represent.
 enum ContainerType {
-  WORKSPACE = 0;
-  HOTSEAT = 1;
-  FOLDER = 2;
-  ALLAPPS = 3;
-  WIDGETS = 4;
-  OVERVIEW = 5;
-  PREDICTION = 6;
-  SEARCHRESULT = 7;
+  DEFAULT_CONTAINERTYPE = 0;
+  WORKSPACE = 1;
+  HOTSEAT = 2;
+  FOLDER = 3;
+  ALLAPPS = 4;
+  WIDGETS = 5;
+  OVERVIEW = 6;
+  PREDICTION = 7;
+  SEARCHRESULT = 8;
 }
 
+// Used to define what type of control a Target would represent.
 enum ControlType {
-  ALL_APPS_BUTTON = 0;
-  WIDGETS_BUTTON = 1;
-  WALLPAPER_BUTTON = 2;
-  SETTINGS_BUTTON = 3;
-  REMOVE_TARGET = 4;
-  UNINSTALL_TARGET = 5;
-  APPINFO_TARGET = 6;
-  RESIZE_HANDLE = 7;
-  FAST_SCROLL_HANDLE = 8;
+  DEFAULT_CONTROLTYPE = 0;
+  ALL_APPS_BUTTON = 1;
+  WIDGETS_BUTTON = 2;
+  WALLPAPER_BUTTON = 3;
+  SETTINGS_BUTTON = 4;
+  REMOVE_TARGET = 5;
+  UNINSTALL_TARGET = 6;
+  APPINFO_TARGET = 7;
+  RESIZE_HANDLE = 8;
+  VERTICAL_SCROLL = 9;
   // HOME, BACK, GO_TO_PLAYSTORE
 }
 
+// Used to define the action component of the LauncherEvent.
 message Action {
   enum Type {
     TOUCH = 0;
@@ -113,10 +120,10 @@
   required Action action = 1;
 
   // List of targets that touch actions can be operated on.
-  optional Target src_target = 2;
-  optional Target dest_target = 3;
+  repeated Target src_target = 2;
+  repeated Target dest_target = 3;
 
   optional int64 action_duration_millis = 4;
   optional int64 elapsed_container_millis = 5;
   optional int64 elapsed_session_millis = 6;
-}
+}
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 19ed41b..6285171 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Instellings"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Laat draai toe"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 8887963..f862e2c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ቅንብሮች"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"ማሽከርከርን ይፍቀዱ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 7c5a3b0..2143539 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"الإعدادات"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"السماح بالتدوير"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 1e041b3..6e05132 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Fırlatmağa icazə verin"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 00a2339..c85bf6b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Разрешаване на завъртането"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index dc6e772..29c218f 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -55,9 +55,9 @@
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রীন"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
     <string name="first_run_cling_title" msgid="2459738000155917941">"স্বাগতম"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"আপনার অ্যাপ্লিকেশান আইকনগুলি অনুলিপি করুন"</string>
+    <string name="migration_cling_title" msgid="9181776667882933767">"আপনার অ্যাপ্লিকেশান আইকনগুলি কপি করুন"</string>
     <string name="migration_cling_description" msgid="2752413805582227644">"আপনার পুরানো হোম স্ক্রীন থেকে আইকন এবং ফোল্ডারগুলি আমদানি করবেন?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"আইকনগুলি অনুলিপি করুন"</string>
+    <string name="migration_cling_copy_apps" msgid="946331230090919440">"আইকনগুলি কপি করুন"</string>
     <string name="migration_cling_use_default" msgid="2626475813981258626">"নতুন করে শুরু করুন"</string>
     <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ওয়ালপেপার, উইজেট এবং সেটিংস"</string>
     <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"কাস্টমাইজ করার জন্য পটভূমিতে আলতো চেপে ধরে রাখুন"</string>
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"উইজেটগুলি"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"সেটিংস"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"ঘূর্ণনের অনুমতি দিন"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 608c3b2..9f72b95 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuració"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permet la rotació"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 1f2c13c..8b3a8ed 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavení"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Povolit otáčení"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 704158d..e69871a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Indstillinger"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Tillad rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4da85e9..186a06a 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Einstellungen"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Drehung zulassen"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 935dc85..4f4e15f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ρυθμίσεις"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Να επιτρέπεται η περιστροφή"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 873cb80..09fc3a2 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 873cb80..09fc3a2 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 873cb80..09fc3a2 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 902cc0e..dd18c1e 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir la rotación"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index de04464..579dd84 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ajustes"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotación"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index c3cf245..371e632 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Seaded"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Luba pööramine"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 99550ee..fa19b45 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ezarpenak"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Baimendu biratzeko aukera"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index eec2c17..148e0cf 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ابزارک‌ها"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"تنظیمات"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"چرخش مجاز است"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 450b3f9..f04896a 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Asetukset"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Salli kierto"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 36ef94e..b323b82 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permettre la rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 449ed68..e6d7a20 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Autoriser la rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 0ebbd17..f402072 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir xiro"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 6e03666..385d41a 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"સેટિંગ્સ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"પરિભ્રમણને મંજૂરી આપો"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 9faabfb..5e99e61 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"शॉर्टकट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके व्यवस्थापक द्वारा अक्षम"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"रोटेशन की अनुमति दें"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"निकालें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d4c1708..8fe238f 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Dopusti rotaciju"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 16cf2c6..d750866 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Beállítások"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Elforgatás engedélyezése"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 70a4aa2..a17216c 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Կարգավորումներ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Թույլ տալ պարբերական կրկնությունը"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c694c86..c9403c2 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Setelan"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Izinkan putaran"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 91bebb5..86f42e8 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Stillingar"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Leyfa snúning"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index af7e3cb..9020719 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Impostazioni"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Consenti rotazione"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 95bc169..1e03cf5 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"רכיבי ווידג\'ט"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"הגדרות"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"אפשרות סיבוב"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 13e4060..fb06f21 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"回転を許可"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 01732ec..b147c03 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"პარამეტრები"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"როტაციის დაშვება"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 001cd3e..61a5dcc 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Артқы фондар"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Айналуға рұқсат ету"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 4656c9c..6f3af8b 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ការកំណត់"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"អនុញ្ញាតឲ្យបង្វិល"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 0511d490..3931359 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"ತಿರುಗಿಸುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ಅಜ್ಞಾತ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index c6413d5..ff0e63f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"설정"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"회전 허용"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 9d635e1..8f7aa0a 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Тууралоолор"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Айлантууга уруксат берүү"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 7f21176..5c61e55 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"ອະ​ນຸ​ຍາດ​ການ​ໝຸນ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index cd15c4d..61437b6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nustatymai"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Leisti kaitaliojimą"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 66318fc..2264675 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Iestatījumi"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Rotācijas atļauja"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 7c44f51..a3b76bb 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Поставки"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволи ротација"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 4d12adb..ac4e9ae 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പറുകൾ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണങ്ങൾ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"തിരിക്കൽ അനുവദിക്കുക"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 7c59295..e9a2077 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Тохиргоо"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Эргүүлэхийг зөвшөөрөх"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 7007ce0..a8ae19b 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग्ज"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"फिरविण्‍यास अनुमती द्या"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 3b57892..e5a15e8 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Tetapan"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Benarkan putaran"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 6048933..ffee1c0 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ဝဒ်ဂျက်များ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"အပြင်အဆင်များ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"လှည့်ရန် ခွင့်ပြုမည်"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index fe40505..be6b644 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Innstillinger"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Tillat rotasjon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 53d3bee..29db37d 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंङहरू"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"परिक्रमणलाई अनुमति दिनुहोस्"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index da20256..4a22bce 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -40,7 +40,7 @@
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelkoppelingen instellen"</string>
+    <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op de startpagina lezen"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"De app toestaan de instellingen en snelkoppelingen op de startpagina te lezen."</string>
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergronden"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Instellingen"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Draaien toestaan"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 11fbe2f..b8a7b43 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟਸ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ਸੈਟਿੰਗਾਂ"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"ਰੋਟੇਸ਼ਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index f94a0dc..ad9fbf4 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ustawienia"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Zezwól na obrót"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 5209fbc..fd6ddee 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Definições"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo administrador"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotação"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 1c54827..e542418 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configurações"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotação"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 0b6c162..eb54ae7 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Setări"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Permiteți rotirea"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6abf971..f73cdf2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Разрешить автоповорот"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Удалить"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 8180a84..89fb60f 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"සැකසීම්"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"කරකැවීමට ඉඩ දෙන්න"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index a5e2500..de017dc 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavenia"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Povoliť otáčanie"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index f484396..c386458 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavitve"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Omogočanje zasuka"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 7087a5e..ccd2c9c 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Cilësimet"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Lejo rotacionin"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6aaa497..82f44b2 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Подешавања"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволи ротацију"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index e38086f..79e8f42 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Inställningar"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Tillåt rotering"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 8460081..58c9ef0 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -73,6 +73,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Mipangilio"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Ruhusu kuzungusha"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index e22b104..ecd4cb0 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"அமைப்பு"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"சுழற்ற அனுமதி"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 5f8059b..402c261 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"సెట్టింగ్‌లు"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"భ్రమణాన్ని అనుమతించండి"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 988d92c..292a95b 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"การตั้งค่า"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"อนุญาตให้ใช้การหมุน"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 1be6eac..5de4a9d 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Mga Setting"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Payagan ang pag-rotate"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 51cbe75..44feebc 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Döndürmeye izin ver"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 7285235..b5d81ee 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Налаштування"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволити обертання"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Видалити"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index 4755c46..bbf9d87 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ترتیبات"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"گردش کی اجازت دیں"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 14989c3..0ae7d7a 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -38,7 +38,7 @@
     <string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Uy"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
-    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirish"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Aylanishga ruxsat berish"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 923b8f8..b3e6234 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Cài đặt"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Cho phép xoay"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index eb5a001..6242644 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -71,6 +71,8 @@
     <string name="widget_button_text" msgid="2880537293434387943">"小部件"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"设置"</string>
+    <!-- no translation found for msg_disabled_by_admin (6898038085516271325) -->
+    <skip />
     <string name="allow_rotation_title" msgid="2118706734511831751">"允许旋转"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c59c714..63e853b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"允許旋轉"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0c39ece..2c6046f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -63,14 +63,15 @@
     <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"按住背景即可自訂"</string>
     <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"輕按即可關閉資料夾"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕按即可儲存新名稱"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"輕觸即可關閉資料夾"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕觸即可儲存新名稱"</string>
     <string name="folder_closed" msgid="4100806530910930934">"資料夾已關閉"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"允許輪播"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 5d76d36..eeffe89 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -71,6 +71,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Izilungiselelo"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
     <string name="allow_rotation_title" msgid="2118706734511831751">"Vumela ukuphenduka"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 8d69f9a..d689f1b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -68,6 +68,10 @@
          filter the activities shown in the launcher. Can be empty. -->
     <string name="app_filter_class" translatable="false"></string>
 
+    <!-- List of package names that com.android.launcher3.action.LAUNCH
+     should be targeting. Can be empty. -->
+    <array name="launch_broadcast_targets" translatable="false"></array>
+
     <!-- Name of an icon provider class. -->
     <string name="icon_provider_class" translatable="false"></string>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6499694..813953e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,12 +24,6 @@
     <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
     <string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
 
-    <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
-    <string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
-
-    <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
-    <string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
-
     <!-- Application name -->
     <string name="app_name">Launcher3</string>
     <!-- Default folder name -->
@@ -170,6 +164,8 @@
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for settings button -->
     <string name="settings_button_text">Settings</string>
+    <!-- Message shown when a feature is disabled by the administrator -->
+    <string name="msg_disabled_by_admin">Disabled by your admin</string>
 
     <!-- Strings for settings -->
     <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index 2cbf914..0000000
--- a/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':WallpaperPicker-Lib'
-project(':WallpaperPicker-Lib').projectDir = new File(rootDir, '../WallpaperPicker')
\ No newline at end of file
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index b5b6897..28fd268 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
 
@@ -87,7 +88,7 @@
         this.componentName = info.getComponentName();
         this.container = ItemInfo.NO_ID;
         flags = initFlags(info);
-        if ((info.getApplicationInfo().flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0) {
+        if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) {
             isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
         }
         if (quietModeEnabled) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index e691b48..bb70be6 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -28,8 +27,12 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
 public class Hotseat extends FrameLayout
-        implements Stats.LaunchSourceProvider{
+        implements UserEventDispatcher.LaunchSourceProvider{
 
     private CellLayout mContent;
 
@@ -157,7 +160,10 @@
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT);
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        targetParent.containerType = LauncherLogProto.HOTSEAT;
     }
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 9ed42f7..effecaf 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -802,7 +802,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 8;
+        private final static int DB_VERSION = 9;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 445831c..a58cddd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -93,7 +93,6 @@
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.PagedView.PageSwitchListener;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -110,11 +109,10 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.logging.LoggerUtils;
-import com.android.launcher3.logging.UserEventLogger;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -138,8 +136,8 @@
  * Default launcher application.
  */
 public class Launcher extends Activity
-        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
-                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
+        implements LauncherExterns, View.OnClickListener, OnLongClickListener,
+                   LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
                    AccessibilityManager.AccessibilityStateChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
@@ -148,6 +146,7 @@
     static final boolean DEBUG_WIDGETS = false;
     static final boolean DEBUG_STRICT_MODE = false;
     static final boolean DEBUG_RESUME_TIME = false;
+    static final boolean DEBUG_LOGGING = false;
 
     static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
 
@@ -174,7 +173,7 @@
     protected static final int REQUEST_LAST = 100;
 
     // To turn on these properties, type
-    // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
+    // adb shell setprop logTap.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
     static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
 
     // The Intent extra that defines whether to ignore the launch animation
@@ -195,10 +194,6 @@
     static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
 
-    static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
-    static final String ACTION_FIRST_LOAD_COMPLETE =
-            "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
-
     private static final String QSB_WIDGET_ID = "qsb_widget_id";
     private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
 
@@ -365,8 +360,7 @@
         int appWidgetId;
     }
 
-    private Stats mStats;
-    private UserEventLogger mUserEventLogger;
+    private UserEventDispatcher mUserEventDispatcher;
 
     public FocusIndicatorView mFocusHandler;
     private boolean mRotationEnabled = false;
@@ -425,9 +419,6 @@
         mDragController = new DragController(this);
         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
 
-        mStats = new Stats(this);
-        initLogger();
-
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
@@ -638,37 +629,41 @@
         }
     }
 
-    public Stats getStats() {
-        return mStats;
-    }
-
     /**
      * Logger object is a singleton and does not have to be coupled with the foreground activity.
      * Since most user event logging is done on the UI, the object is retrieved from the
      * callback for convenience.
      */
-    private void initLogger() {
-        if (mLauncherCallbacks != null) {
-            mUserEventLogger = mLauncherCallbacks.getLogger();
-        }
-        if (mUserEventLogger == null) {
-            mUserEventLogger = new UserEventLogger() {
-                @Override
-                public void processEvent(LauncherLogProto.LauncherEvent ev) {
-                    if (ev.action.touch == LauncherLogProto.Action.TAP && ev.srcTarget.itemType == LauncherLogProto.APP_ICON) {
-                        Log.d(TAG, String.format(Locale.US, "action:%s target:%s\n\telapsed container %d ms session %d ms",
-                                LoggerUtils.getActionStr(ev.action),
-                                LoggerUtils.getTargetStr(ev.srcTarget),
-                                ev.elapsedContainerMillis,
-                                ev.elapsedSessionMillis));
-                    }
+    private UserEventDispatcher createUserEventDispatcher() {
+        return new UserEventDispatcher() {
+            @Override
+            public void dispatchUserEvent(LauncherLogProto.LauncherEvent ev, Intent intent) {
+                if (!DEBUG_LOGGING) {
+                    return;
                 }
-            };
-        }
+                Log.d("UserEvent", String.format(Locale.US,
+                        "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms",
+                        LoggerUtils.getActionStr(ev.action),
+                        LoggerUtils.getTargetStr(ev.srcTarget[0]),
+                        LoggerUtils.getTargetStr(ev.srcTarget[1]),
+                        ev.elapsedContainerMillis,
+                        ev.elapsedSessionMillis));
+            }
+        };
     }
 
-    public UserEventLogger getLogger() {
-        return mUserEventLogger;
+    public UserEventDispatcher getUserEventDispatcher() {
+        if (mLauncherCallbacks != null) {
+            UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
+            if (dispatcher != null) {
+                return dispatcher;
+            }
+        }
+
+        if (mUserEventDispatcher == null) {
+            mUserEventDispatcher = createUserEventDispatcher();
+        }
+        return mUserEventDispatcher;
     }
 
     public boolean isDraggingEnabled() {
@@ -992,7 +987,7 @@
         }
 
         super.onResume();
-        mUserEventLogger.resetElapsedSessionMillis();
+        getUserEventDispatcher().resetElapsedSessionMillis();
 
         // Restore the previous launcher state
         if (mOnResumeState == State.WORKSPACE) {
@@ -1347,7 +1342,6 @@
         mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mWorkspace.setPageSwitchListener(this);
         mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1842,7 +1836,7 @@
         return mModel;
     }
 
-    protected SharedPreferences getSharedPrefs() {
+    public SharedPreferences getSharedPrefs() {
         return mSharedPrefs;
     }
 
@@ -2596,10 +2590,6 @@
         if (!isAppsViewVisible()) {
             showAppsView(true /* animated */, false /* resetListToTop */,
                     true /* updatePredictedApps */, false /* focusSearchBar */);
-
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAllAppsButton(v);
-            }
         }
     }
 
@@ -2673,10 +2663,6 @@
 
         // Start activities
         startAppShortcutOrInfoActivity(v);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickAppShortcut(v);
-        }
     }
 
     @Thunk void startAppShortcutOrInfoActivity(View v) {
@@ -2699,7 +2685,7 @@
         }
 
         boolean success = startActivitySafely(v, intent, tag);
-        mStats.recordLaunch(v, intent, shortcut);
+        getUserEventDispatcher().logAppLaunch(v, intent);
 
         if (success && v instanceof BubbleTextView) {
             mWaitingForResume = (BubbleTextView) v;
@@ -2721,11 +2707,7 @@
         FolderIcon folderIcon = (FolderIcon) v;
         if (!folderIcon.getFolderInfo().opened && !folderIcon.getFolder().isDestroyed()) {
             // Open the requested folder
-            openFolder(folderIcon, true);
-        }
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickFolderIcon(v);
+            openFolder(folderIcon);
         }
     }
 
@@ -2739,9 +2721,6 @@
             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
         } else {
             showWidgetsView(true /* animated */, true /* resetPageToZero */);
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAddWidgetButton(view);
-            }
         }
     }
 
@@ -2750,16 +2729,15 @@
      * on the home screen.
      */
     protected void onClickWallpaperPicker(View v) {
+        if (!Utilities.isWallapaperAllowed(this)) {
+            Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
+            return;
+        }
+
         if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
         int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
         float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
-        startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName())
-                        .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset),
-                REQUEST_PICK_WALLPAPER);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickWallpaperPicker(v);
-        }
+        // TODO: Start the system wallpaper picker
     }
 
     /**
@@ -2802,10 +2780,6 @@
             // content screen, move to default.
             moveWorkspaceToDefaultScreen();
         }
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onDragStarted(view);
-        }
     }
 
     /**
@@ -2996,7 +2970,7 @@
         }
     }
 
-    private void growAndFadeOutFolderIcon(FolderIcon fi, boolean animate) {
+    private void growAndFadeOutFolderIcon(FolderIcon fi) {
         if (fi == null) return;
         FolderInfo info = (FolderInfo) fi.getTag();
         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -3016,9 +2990,6 @@
         }
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.start();
-        if (!animate) {
-            oa.end();
-        }
     }
 
     private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
@@ -3058,8 +3029,7 @@
      *
      * @param folderIcon The FolderIcon describing the folder to open.
      */
-    public void openFolder(FolderIcon folderIcon, boolean animate) {
-        animate &= !Utilities.isPowerSaverOn(this);
+    public void openFolder(FolderIcon folderIcon) {
 
         Folder folder = folderIcon.getFolder();
         Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
@@ -3084,12 +3054,9 @@
             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
                     folder.getParent() + ").");
         }
-        if (animate) {
-            folder.animateOpen();
-        } else {
-            folder.open();
-        }
-        growAndFadeOutFolderIcon(folderIcon, animate);
+        folder.animateOpen();
+
+        growAndFadeOutFolderIcon(folderIcon);
 
         // Notify the accessibility manager that this folder "window" has appeared and occluded
         // the workspace items
@@ -3495,6 +3462,7 @@
             List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
                 mAppsView.setPredictedApps(apps);
+                getUserEventDispatcher().setPredictedApps(apps);
             }
         }
     }
@@ -4110,7 +4078,6 @@
         mWorkspace.restoreInstanceStateForRemainingPages();
 
         setWorkspaceLoading(false);
-        sendLoadingCompleteBroadcastIfNecessary();
 
         // If we received the result of any pending adds while the loader was running (e.g. the
         // widget configuration forced an orientation change), process them now.
@@ -4136,18 +4103,6 @@
         }
     }
 
-    private void sendLoadingCompleteBroadcastIfNecessary() {
-        if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
-            String permission =
-                    getResources().getString(R.string.receive_first_load_broadcast_permission);
-            Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
-            sendBroadcast(intent, permission);
-            SharedPreferences.Editor editor = mSharedPrefs.edit();
-            editor.putBoolean(FIRST_LOAD_COMPLETE, true);
-            editor.apply();
-        }
-    }
-
     public boolean isAllAppsButtonRank(int rank) {
         if (mHotseat != null) {
             return mHotseat.isAllAppsButtonRank(rank);
@@ -4443,9 +4398,6 @@
     }
 
     protected boolean isLauncherPreinstalled() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.isLauncherPreinstalled();
-        }
         PackageManager pm = getPackageManager();
         try {
             ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0);
@@ -4651,13 +4603,6 @@
         mWorkspace.moveToDefaultScreen(false);
     }
 
-    @Override
-    public void onPageSwitch(View newPage, int newPageIndex) {
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onPageSwitch(newPage, newPageIndex);
-        }
-    }
-
     /**
      * Returns a FastBitmapDrawable with the icon, accurately sized.
      */
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 635d413..34117b6 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -1,14 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.launcher3;
 
-import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
-import android.view.ViewGroup;
+
 import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventLogger;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -61,35 +76,14 @@
     public void onInteractionBegin();
     public void onInteractionEnd();
 
-    /**
-     * Extension points for Gel Logging.
-     */
-    @Deprecated
-    public void onClickAllAppsButton(View v);
-    @Deprecated
-    public void onClickFolderIcon(View v);
-    @Deprecated
-    public void onClickAppShortcut(View v);
-    @Deprecated
-    public void onClickPagedViewIcon(View v);
-    @Deprecated
-    public void onClickWallpaperPicker(View v);
     @Deprecated
     public void onClickSettingsButton(View v);
     @Deprecated
-    public void onClickAddWidgetButton(View v);
-    @Deprecated
-    public void onPageSwitch(View newPage, int newPageIndex);
-    @Deprecated
     public void onWorkspaceLockedChanged();
-    @Deprecated
-    public void onDragStarted(View view);
 
     public boolean providesSearch();
     public boolean startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, Rect sourceBounds);
-    @Deprecated
-    public boolean startSearchFromAllApps(String query);
     public boolean hasCustomContentToLeft();
     public void populateCustomContentContainer();
     public View getQsbBar();
@@ -98,16 +92,13 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
-    public UserEventLogger getLogger();
+    public UserEventDispatcher getUserEventDispatcher();
     public Intent getFirstRunActivity();
     public boolean hasFirstRunActivity();
     public boolean hasDismissableIntroScreen();
     public View getIntroScreen();
     public boolean shouldMoveToDefaultScreenOnHomeIntent();
     public boolean hasSettings();
-    @Deprecated
-    public boolean overrideWallpaperDimensions();
-    public boolean isLauncherPreinstalled();
     public AllAppsSearchBarController getAllAppsSearchBarController();
     public List<ComponentKey> getPredictedApps();
     public static final int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
new file mode 100644
index 0000000..c7a8668
--- /dev/null
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.SharedPreferences;
+
+/**
+ * This interface defines the set of methods that the Launcher activity exposes. Methods
+ * here should be safe to call from classes outside of com.android.launcher3.*
+ */
+public interface LauncherExterns {
+
+    public boolean setLauncherCallbacks(LauncherCallbacks callbacks);
+
+    public SharedPreferences getSharedPrefs();
+
+    public void setLauncherOverlay(Launcher.LauncherOverlay overlay);
+}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 6ce2293..adb5031 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -39,8 +39,8 @@
 
     // TODO: Delete these files on upgrade
     public static final List<String> OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList(
-            "launches.log",
-            "stats.log",
+            "launches.logTap",
+            "stats.logTap",
             "launcher.preferences",
             "com.android.launcher3.compat.PackageInstallerCompatV16.queue"));
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f2b307b..884685c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -33,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -64,6 +63,7 @@
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.StringFilter;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
@@ -1749,6 +1749,7 @@
                     long serialNumber;
                     Intent intent;
                     UserHandleCompat user;
+                    String targetPackage;
 
                     while (!mStopped && c.moveToNext()) {
                         try {
@@ -1767,6 +1768,7 @@
                                 int promiseType = c.getInt(restoredIndex);
                                 int disabledState = 0;
                                 boolean itemReplaced = false;
+                                targetPackage = null;
                                 if (user == null) {
                                     // User has been deleted remove the item.
                                     itemsToRemove.add(id);
@@ -1780,6 +1782,9 @@
                                                 cn.getPackageName(), user);
                                         boolean validComponent = validPkg &&
                                                 launcherApps.isActivityEnabledForProfile(cn, user);
+                                        if (validPkg) {
+                                            targetPackage = cn.getPackageName();
+                                        }
 
                                         if (validComponent) {
                                             if (restored) {
@@ -1787,13 +1792,8 @@
                                                 restoredRows.add(id);
                                                 restored = false;
                                             }
-                                            boolean isSuspended = launcherApps.isPackageSuspendedForProfile(
-                                                    cn.getPackageName(), user);
-                                            if (isSuspended) {
-                                                disabledState = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
-                                            }
                                             if (quietMode.get(serialNumber)) {
-                                                disabledState |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+                                                disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
                                             }
                                         } else if (validPkg) {
                                             intent = null;
@@ -1859,9 +1859,8 @@
                                                 itemsToRemove.add(id);
                                                 continue;
                                             }
-                                        } else if (launcherApps.isAppEnabled(
-                                                manager, cn.getPackageName(),
-                                                PackageManager.GET_UNINSTALLED_PACKAGES)) {
+                                        } else if (PackageManagerHelper.isAppOnSdcard(
+                                                manager, cn.getPackageName())) {
                                             // Package is present but not available.
                                             allowMissingTarget = true;
                                             disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
@@ -1901,7 +1900,7 @@
 
                                 if (itemReplaced) {
                                     if (user.equals(UserHandleCompat.myUserHandle())) {
-                                        info = getAppShortcutInfo(manager, intent, user, context, null,
+                                        info = getAppShortcutInfo(intent, user, context, null,
                                                 cursorIconInfo.iconIndex, titleIndex,
                                                 false, useLowResIcon);
                                     } else {
@@ -1921,12 +1920,17 @@
                                     }
                                 } else if (itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getAppShortcutInfo(manager, intent, user, context, c,
+                                    info = getAppShortcutInfo(intent, user, context, c,
                                             cursorIconInfo.iconIndex, titleIndex,
                                             allowMissingTarget, useLowResIcon);
                                 } else {
                                     info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
 
+                                    // Shortcuts are only available on the primary profile
+                                    if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
+                                        disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+                                    }
+
                                     // App shortcuts that used to be automatically added to Launcher
                                     // didn't always have the correct intent flags set, so do that
                                     // here
@@ -1954,7 +1958,7 @@
                                     if (info.promisedIntent != null) {
                                         info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
                                     }
-                                    info.isDisabled = disabledState;
+                                    info.isDisabled |= disabledState;
                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
                                     }
@@ -2822,9 +2826,7 @@
                     packagesUnavailable.clear();
                     for (String pkg : entry.getValue()) {
                         if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
-                            boolean packageOnSdcard = launcherApps.isAppEnabled(
-                                    manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
-                            if (packageOnSdcard) {
+                            if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
                                 packagesUnavailable.add(pkg);
                             } else {
                                 packagesRemoved.add(pkg);
@@ -3310,7 +3312,7 @@
      *
      * If c is not null, then it will be used to fill in missing data like the title and icon.
      */
-    public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
+    public ShortcutInfo getAppShortcutInfo(Intent intent,
             UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
             boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
@@ -3340,6 +3342,10 @@
             info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
         }
 
+        if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+            info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+        }
+
         // from the db
         if (TextUtils.isEmpty(info.title) && c != null) {
             info.title =  Utilities.trim(c.getString(titleIndex));
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f076094..a62d1e7 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -658,13 +658,10 @@
                     "spanY INTEGER," +
                     "itemType INTEGER," +
                     "appWidgetId INTEGER NOT NULL DEFAULT -1," +
-                    "isShortcut INTEGER," +
                     "iconType INTEGER," +
                     "iconPackage TEXT," +
                     "iconResource TEXT," +
                     "icon BLOB," +
-                    "uri TEXT," +
-                    "displayMode INTEGER," +
                     "appWidgetProvider TEXT," +
                     "modified INTEGER NOT NULL DEFAULT 0," +
                     "restored INTEGER NOT NULL DEFAULT 0," +
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 0c16287..0e559a5 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -216,16 +216,6 @@
         public static final int ITEM_TYPE_FOLDER = 2;
 
         /**
-        * The favorite is a live folder
-        *
-        * Note: live folders can no longer be added to Launcher, and any live folders which
-        * exist within the launcher database will be ignored when loading.  That said, these
-        * entries in the database may still exist, and are not automatically stripped.
-        */
-        @Deprecated
-        static final int ITEM_TYPE_LIVE_FOLDER = 3;
-
-        /**
          * The favorite is a widget
          */
         public static final int ITEM_TYPE_APPWIDGET = 4;
@@ -236,24 +226,6 @@
         public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
 
         /**
-         * The favorite is a clock
-         */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
-
-        /**
-         * The favorite is a search widget
-         */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_SEARCH = 1001;
-
-        /**
-         * The favorite is a photo frame
-         */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
-
-        /**
          * The appWidgetId of the widget
          *
          * <P>Type: INTEGER</P>
@@ -266,33 +238,6 @@
          * <P>Type: STRING</P>
          */
         public static final String APPWIDGET_PROVIDER = "appWidgetProvider";
-        
-        /**
-         * Indicates whether this favorite is an application-created shortcut or not.
-         * If the value is 0, the favorite is not an application-created shortcut, if the
-         * value is 1, it is an application-created shortcut.
-         * <P>Type: INTEGER</P>
-         */
-        @Deprecated
-        static final String IS_SHORTCUT = "isShortcut";
-
-        /**
-         * The URI associated with the favorite. It is used, for instance, by
-         * live folders to find the content provider.
-         * <P>Type: TEXT</P>
-         */
-        @Deprecated
-        static final String URI = "uri";
-
-        /**
-         * The display mode if the item is a live folder.
-         * <P>Type: INTEGER</P>
-         *
-         * @see android.provider.LiveFolders#DISPLAY_MODE_GRID
-         * @see android.provider.LiveFolders#DISPLAY_MODE_LIST
-         */
-        @Deprecated
-        static final String DISPLAY_MODE = "displayMode";
 
         /**
          * Boolean indicating that his item was restored and not yet successfully bound.
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index c5ae9da..5692046 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -148,7 +148,7 @@
             }
             @Override
             void onTransitionComplete() {
-                mLauncher.getLogger().resetElapsedContainerMillis();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
                 if (startSearchAfterTransition) {
                     toView.startAppsSearch();
                 }
@@ -171,7 +171,7 @@
                 new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
                     @Override
                     void onTransitionComplete() {
-                        mLauncher.getLogger().resetElapsedContainerMillis();
+                        mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
                     }
                 });
     }
@@ -470,7 +470,7 @@
             }
             @Override
             void onTransitionComplete() {
-                mLauncher.getLogger().resetElapsedContainerMillis();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
@@ -500,7 +500,7 @@
             }
             @Override
             void onTransitionComplete() {
-                mLauncher.getLogger().resetElapsedContainerMillis();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index daeb4c1..6f7566f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -142,8 +142,6 @@
 
     protected int mActivePointerId = INVALID_POINTER;
 
-    private PageSwitchListener mPageSwitchListener;
-
     // If true, modify alpha of neighboring pages as user scrolls left/right
     protected boolean mFadeInAdjacentScreens = false;
 
@@ -195,10 +193,6 @@
     private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
     private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
 
-    public interface PageSwitchListener {
-        void onPageSwitch(View newPage, int newPageIndex);
-    }
-
     public PagedView(Context context) {
         this(context, null);
     }
@@ -365,17 +359,6 @@
     }
 
     /**
-     * Add a page change listener which will be called when a page is _finished_ listening.
-     *
-     */
-    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
-        mPageSwitchListener = pageSwitchListener;
-        if (mPageSwitchListener != null) {
-            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
-        }
-    }
-
-    /**
      * Returns the index of the currently displayed page. When in free scroll mode, this is the page
      * that the user was on before entering free scroll mode (e.g. the home screen page they
      * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()}
@@ -484,10 +467,6 @@
      * has settled.
      */
     protected void notifyPageSwitchListener() {
-        if (mPageSwitchListener != null) {
-            mPageSwitchListener.onPageSwitch(getPageAt(getNextPage()), getNextPage());
-        }
-
         updatePageIndicator();
     }
 
@@ -1565,8 +1544,6 @@
             } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
                 setCurrentPage(mTempVisiblePagesRange[1]);
             }
-        } else {
-            setCurrentPage(getPageNearestToCenterOfScreen());
         }
 
         setEnableOverscroll(!freeScroll);
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
deleted file mode 100644
index 10a26ad..0000000
--- a/src/com/android/launcher3/Stats.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-
-import com.android.launcher3.config.ProviderConfig;
-
-public class Stats {
-
-    /**
-     * Implemented by containers to provide a launch source for a given child.
-     */
-    public interface LaunchSourceProvider {
-        void fillInLaunchSourceData(View v, Bundle sourceData);
-    }
-
-    /**
-     * Helpers to add the source to a launch intent.
-     */
-    public static class LaunchSourceUtils {
-        /**
-         * Create a default bundle for LaunchSourceProviders to fill in their data.
-         */
-        public static Bundle createSourceData() {
-            Bundle sourceData = new Bundle();
-            sourceData.putString(SOURCE_EXTRA_CONTAINER, CONTAINER_HOMESCREEN);
-            // Have default container/sub container pages
-            sourceData.putInt(SOURCE_EXTRA_CONTAINER_PAGE, 0);
-            sourceData.putInt(SOURCE_EXTRA_SUB_CONTAINER_PAGE, 0);
-            return sourceData;
-        }
-
-        /**
-         * Finds the next launch source provider in the parents of the view hierarchy and populates
-         * the source data from that provider.
-         */
-        public static void populateSourceDataFromAncestorProvider(View v, Bundle sourceData) {
-            if (v == null) {
-                return;
-            }
-
-            Stats.LaunchSourceProvider provider = null;
-            ViewParent parent = v.getParent();
-            while (parent != null && parent instanceof View) {
-                if (parent instanceof Stats.LaunchSourceProvider) {
-                    provider = (Stats.LaunchSourceProvider) parent;
-                    break;
-                }
-                parent = parent.getParent();
-            }
-
-            if (provider != null) {
-                provider.fillInLaunchSourceData(v, sourceData);
-            } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                throw new RuntimeException("Expected LaunchSourceProvider");
-            }
-        }
-    }
-
-    private static final boolean DEBUG_BROADCASTS = false;
-
-    public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
-    public static final String EXTRA_INTENT = "intent";
-    public static final String EXTRA_CONTAINER = "container";
-    public static final String EXTRA_SCREEN = "screen";
-    public static final String EXTRA_CELLX = "cellX";
-    public static final String EXTRA_CELLY = "cellY";
-    public static final String EXTRA_SOURCE = "source";
-
-    public static final String SOURCE_EXTRA_CONTAINER = "container";
-    public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page";
-    public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container";
-    public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page";
-
-    public static final String CONTAINER_SEARCH_BOX = "search_box";
-    public static final String CONTAINER_ALL_APPS = "all_apps";
-    public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace
-    public static final String CONTAINER_HOTSEAT = "hotseat";
-
-    public static final String SUB_CONTAINER_FOLDER = "folder";
-    public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z";
-    public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction";
-    public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search";
-
-    private final Launcher mLauncher;
-    private final String mLaunchBroadcastPermission;
-
-    public Stats(Launcher launcher) {
-        mLauncher = launcher;
-        mLaunchBroadcastPermission =
-                launcher.getResources().getString(R.string.receive_launch_broadcasts_permission);
-
-        if (DEBUG_BROADCASTS) {
-            launcher.registerReceiver(
-                    new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
-                                    + intent.getStringExtra(EXTRA_INTENT));
-                        }
-                    },
-                    new IntentFilter(ACTION_LAUNCH),
-                    mLaunchBroadcastPermission,
-                    null
-            );
-        }
-    }
-
-    public void recordLaunch(View v, Intent intent, ShortcutInfo shortcut) {
-        intent = new Intent(intent);
-        intent.setSourceBounds(null);
-
-        final String flat = intent.toUri(0);
-        Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat);
-        if (shortcut != null) {
-            broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container)
-                    .putExtra(EXTRA_SCREEN, shortcut.screenId)
-                    .putExtra(EXTRA_CELLX, shortcut.cellX)
-                    .putExtra(EXTRA_CELLY, shortcut.cellY);
-        }
-
-        Bundle sourceExtras = LaunchSourceUtils.createSourceData();
-        LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras);
-        broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras);
-        mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
-        if (intent.getComponent() != null) {
-            mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3969d30..d6d3517 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -19,6 +19,7 @@
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.SearchManager;
+import android.app.WallpaperManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
@@ -45,9 +46,9 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.PowerManager;
-import android.os.Process;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -62,7 +63,6 @@
 
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.IconNormalizer;
 
 import java.io.ByteArrayOutputStream;
@@ -728,13 +728,6 @@
                 (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
     }
 
-    public static void assertWorkerThread() {
-        if (ProviderConfig.IS_DOGFOOD_BUILD &&
-                (LauncherModel.sWorkerThread.getThreadId() != Process.myTid())) {
-            throw new IllegalStateException();
-        }
-    }
-
     /**
      * Returns true if the intent is a valid launch intent for a launcher activity of an app.
      * This is used to identify shortcuts which are different from the ones exposed by the
@@ -837,6 +830,17 @@
         return ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
     }
 
+    public static boolean isWallapaperAllowed(Context context) {
+        if (isNycOrAbove()) {
+            try {
+                WallpaperManager wm = context.getSystemService(WallpaperManager.class);
+                return (Boolean) wm.getClass().getDeclaredMethod("isWallpaperSettingAllowed")
+                        .invoke(wm);
+            } catch (Exception e) { }
+        }
+        return true;
+    }
+
     /**
      * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
      * This allows the badging to be done based on the action bitmap size rather than
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
deleted file mode 100644
index 62b9be4..0000000
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Bundle;
-import android.util.Pair;
-
-import com.android.wallpaperpicker.tileinfo.DefaultWallpaperInfo;
-import com.android.wallpaperpicker.tileinfo.FileWallpaperInfo;
-import com.android.wallpaperpicker.tileinfo.WallpaperTileInfo;
-
-import java.io.File;
-import java.util.ArrayList;
-
-public class WallpaperPickerActivity extends com.android.wallpaperpicker.WallpaperPickerActivity {
-
-    @Override
-    public void startActivityForResultSafely(Intent intent, int requestCode) {
-        Utilities.startActivityForResultSafely(this, intent, requestCode);
-    }
-
-    @Override
-    public boolean enableRotation() {
-        return super.enableRotation() ||
-                getContentResolver().call(LauncherSettings.Settings.CONTENT_URI,
-                        LauncherSettings.Settings.METHOD_GET_BOOLEAN,
-                        Utilities.ALLOW_ROTATION_PREFERENCE_KEY, new Bundle())
-                        .getBoolean(LauncherSettings.Settings.EXTRA_VALUE);
-    }
-
-    @Override
-    public ArrayList<WallpaperTileInfo> findBundledWallpapers() {
-        final PackageManager pm = getPackageManager();
-        final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
-
-        Partner partner = Partner.get(pm);
-        if (partner != null) {
-            final Resources partnerRes = partner.getResources();
-            final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
-                    partner.getPackageName());
-            if (resId != 0) {
-                addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
-            }
-
-            // Add system wallpapers
-            File systemDir = partner.getWallpaperDirectory();
-            if (systemDir != null && systemDir.isDirectory()) {
-                for (File file : systemDir.listFiles()) {
-                    if (!file.isFile()) {
-                        continue;
-                    }
-                    String name = file.getName();
-                    int dotPos = name.lastIndexOf('.');
-                    String extension = "";
-                    if (dotPos >= -1) {
-                        extension = name.substring(dotPos);
-                        name = name.substring(0, dotPos);
-                    }
-
-                    if (name.endsWith("_small")) {
-                        // it is a thumbnail
-                        continue;
-                    }
-
-                    File thumbnail = new File(systemDir, name + "_small" + extension);
-                    Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
-                    if (thumb != null) {
-                        bundled.add(new FileWallpaperInfo(
-                                file, new BitmapDrawable(getResources(), thumb)));
-                    }
-                }
-            }
-        }
-
-        Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
-        if (r != null) {
-            try {
-                Resources wallpaperRes = pm.getResourcesForApplication(r.first);
-                addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
-            } catch (PackageManager.NameNotFoundException e) {
-            }
-        }
-
-        if (partner == null || !partner.hideDefaultWallpaper()) {
-            // Add an entry for the default wallpaper (stored in system resources)
-            WallpaperTileInfo defaultWallpaperInfo = DefaultWallpaperInfo.get(this);
-            if (defaultWallpaperInfo != null) {
-                bundled.add(0, defaultWallpaperInfo);
-            }
-        }
-        return bundled;
-    }
-}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index cb3126c..d9bd782 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SQLiteCacheHelper;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetCell;
@@ -169,7 +170,7 @@
      * This ensures that we remove entries for packages which changed while the launcher was dead.
      */
     public void removeObsoletePreviews(ArrayList<? extends ComponentKey> list) {
-        Utilities.assertWorkerThread();
+        Preconditions.assertWorkerThread();
 
         LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f04244f..d55e124 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -39,7 +39,6 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcelable;
@@ -73,6 +72,9 @@
 import com.android.launcher3.dragndrop.SpringLoadedDragController;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
@@ -92,7 +94,7 @@
 public class Workspace extends PagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
-        Insettable, DropTargetSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
+        Insettable, DropTargetSource, AccessibilityDragSource, UserEventDispatcher.LaunchSourceProvider {
     private static final String TAG = "Launcher.Workspace";
 
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
@@ -4255,14 +4257,21 @@
             }
             nScreens--;
         }
+        if (nScreens == 0) {
+            // When the workspace is not loaded, we do not know how many screen will be bound.
+            return getContext().getString(R.string.all_apps_home_button_label);
+        }
         return getContext().getString(R.string.workspace_scroll_format,
                 page + 1 - delta, nScreens);
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN);
-        sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage());
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = getCurrentPage();
+        targetParent.containerType = LauncherLogProto.WORKSPACE;
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2b3d061..8d5ade3 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,7 +19,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.View;
@@ -27,17 +26,19 @@
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.Stats;
 import com.android.launcher3.Utilities;
-
+import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import java.util.List;
 
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
 public class AllAppsRecyclerView extends BaseRecyclerView
-        implements Stats.LaunchSourceProvider {
+        implements LaunchSourceProvider {
 
     private AlphabeticalAppsList mApps;
     private AllAppsFastScrollHelper mFastScrollHelper;
@@ -165,11 +166,9 @@
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
         if (mApps.hasFilter()) {
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_SEARCH);
+            targetParent.containerType = LauncherLogProto.SEARCHRESULT;
         } else {
             if (v instanceof BubbleTextView) {
                 BubbleTextView icon = (BubbleTextView) v;
@@ -178,14 +177,13 @@
                     List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
                     AlphabeticalAppsList.AdapterItem item = items.get(position);
                     if (item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
-                        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                                Stats.SUB_CONTAINER_ALL_APPS_PREDICTION);
+                        targetParent.containerType = LauncherLogProto.PREDICTION;
                         return;
                     }
                 }
             }
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_A_Z);
+
+            targetParent.containerType = LauncherLogProto.ALLAPPS;
         }
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index aaf756e..0bc9588 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -24,8 +24,6 @@
 
 public abstract class LauncherActivityInfoCompat {
 
-    public static final int FLAG_SUSPENDED = 1<<30;
-
     LauncherActivityInfoCompat() {
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index bc900bc..237a9e9 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,9 +19,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
 import android.os.Bundle;
 
@@ -59,9 +56,7 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isNycOrAbove()) {
-                    sInstance = new LauncherAppsCompatVN(context.getApplicationContext());
-                } else if (Utilities.ATLEAST_LOLLIPOP) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
@@ -84,13 +79,4 @@
     public abstract boolean isActivityEnabledForProfile(ComponentName component,
             UserHandleCompat user);
     public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
-
-    public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
-        try {
-            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
-            return info != null && info.enabled;
-        } catch (NameNotFoundException e) {
-            return false;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 2d0778d..4e2fc05 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -27,11 +27,11 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -114,7 +114,7 @@
     }
 
     public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
-        return isAppEnabled(mPm, packageName, 0);
+        return PackageManagerHelper.isAppEnabled(mPm, packageName);
     }
 
     public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVN.java b/src/com/android/launcher3/compat/LauncherAppsCompatVN.java
deleted file mode 100644
index 0d883b6..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVN.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherApps;
-import android.os.UserHandle;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N)
-public class LauncherAppsCompatVN extends LauncherAppsCompatVL {
-
-    private static final String TAG = "LauncherAppsCompatVN";
-
-    LauncherAppsCompatVN(Context context) {
-        super(context);
-    }
-
-    @Override
-    public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
-        if (user != null && packageName != null) {
-            try {
-                //TODO: Replace with proper API call once google3 SDK is updated.
-                Method getApplicationInfoMethod = LauncherApps.class.getMethod("getApplicationInfo",
-                        String.class, int.class, UserHandle.class);
-
-                ApplicationInfo info = (ApplicationInfo) getApplicationInfoMethod.invoke(
-                        mLauncherApps, packageName, 0, user.getUser());
-                if (info != null) {
-                    return (info.flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0;
-                }
-            } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException
-                    | IllegalArgumentException | InvocationTargetException e) {
-                Log.e(TAG, "Running on N without getApplicationInfo", e);
-            }
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 09a92a9..7dc8155 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -29,7 +29,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Bundle;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
@@ -70,7 +69,6 @@
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Stats;
 import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
@@ -79,6 +77,9 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.UiThreadCircularReveal;
 
@@ -92,7 +93,7 @@
 public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
         View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource,
-        Stats.LaunchSourceProvider {
+        LaunchSourceProvider {
     private static final String TAG = "Launcher.Folder";
 
     /**
@@ -644,29 +645,6 @@
         mContent.verifyVisibleHighResIcons(mContent.getNextPage());
     }
 
-    /**
-     * Opens the folder without any animation
-     */
-    public void open() {
-        if (!(getParent() instanceof DragLayer)) return;
-
-        mContent.completePendingPageChanges();
-        if (!mDragInProgress) {
-            // Open on the first page.
-            mContent.snapToPageImmediately(0);
-        }
-        centerAboutIcon();
-        mFolderName.setTranslationX(0);
-        mContent.setMarkerScale(1);
-
-        // Make sure the folder picks up the last drag move even if the finger doesn't move.
-        if (mDragController.isDragging()) {
-            mDragController.forceTouchMove();
-        }
-
-        mContent.verifyVisibleHighResIcons(mContent.getNextPage());
-    }
-
     public void beginExternalDrag(ShortcutInfo item) {
         mCurrentDragInfo = item;
         mEmptyCellRank = mContent.allocateRankForNewItem(item);
@@ -1439,11 +1417,12 @@
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        // Fill in from the folder icon's launch source provider first
-        Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData);
-        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER);
-        sourceData.putInt(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE, mContent.getCurrentPage());
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = mContent.getCurrentPage();
+        targetParent.containerType = LauncherLogProto.FOLDER;
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index d76608a..1e4eb7f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -258,7 +258,7 @@
                 item = (ShortcutInfo) mDragInfo;
             }
             mFolder.beginExternalDrag(item);
-            mLauncher.openFolder(FolderIcon.this, true);
+            mLauncher.openFolder(FolderIcon.this);
         }
     };
 
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 4b30384..584e38e 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -5,7 +5,6 @@
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Stats;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -18,16 +17,6 @@
  */
 public class LoggerUtils {
     private static final String TAG = "LoggerUtils";
-    private static final boolean DEBUG = false;
-
-    static int getContainerType(ShortcutInfo shortcut) {
-        switch ((int) shortcut.container) {
-            case LauncherSettings.Favorites.CONTAINER_DESKTOP: return LauncherLogProto.WORKSPACE;
-            case LauncherSettings.Favorites.CONTAINER_HOTSEAT: return LauncherLogProto.HOTSEAT;
-            default:
-                return (int) shortcut.container;
-        }
-    }
 
     public static String getActionStr(LauncherLogProto.Action action) {
         switch(action.touch) {
@@ -62,8 +51,10 @@
             default: typeStr = "UNKNOWN";
         }
 
-        return typeStr + " " + t.packageNameHash + " grid=(" + t.gridX + "," + t.gridY + ") "
-                + getContainerStr(t.parent);
+        return typeStr + ", packageHash=" + t.packageNameHash
+                + ", componentHash=" + t.componentHash
+                + ", intentHash=" + t.intentHash
+                + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
     }
 
     private static String getControlStr(Target t) {
@@ -76,7 +67,6 @@
             case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET";
             case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET";
             case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE";
-            case LauncherLogProto.FAST_SCROLL_HANDLE: return "FAST_SCROLL_HANDLE";
             default: return "UNKNOWN";
         }
     }
@@ -114,4 +104,22 @@
         }
         return str + " id=" + t.pageIndex;
     }
+
+
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            int childTargetType,
+            int parentTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[2];
+        event.srcTarget[0] = new LauncherLogProto.Target();
+        event.srcTarget[0].type = childTargetType;
+        event.srcTarget[1] = new LauncherLogProto.Target();
+        event.srcTarget[1].type = parentTargetType;
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
 }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
new file mode 100644
index 0000000..89ad075
--- /dev/null
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.logging;
+
+import android.content.Intent;
+import android.view.View;
+import android.view.ViewParent;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.ComponentKey;
+
+import java.util.List;
+
+/**
+ * Manages the creation of {@link LauncherEvent}.
+ */
+public abstract class UserEventDispatcher {
+
+    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+    /**
+     * Implemented by containers to provide a launch source for a given child.
+     */
+    public interface LaunchSourceProvider {
+
+        /**
+         * Copies data from the source to the destination proto.
+         * @param v                 source of the data
+         * @param info          source of the data
+         * @param target            dest of the data
+         * @param targetParent      dest of the data
+         */
+        void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent);
+    }
+
+    /**
+     * Recursively finds the parent of the given child which implements IconLogInfoProvider
+     */
+    public static LaunchSourceProvider getLaunchProviderRecursive(View v) {
+        ViewParent parent = null;
+        if (v != null) {
+            parent = v.getParent();
+        } else {
+            return null;
+        }
+
+        // Optimization to only check up to 5 parents.
+        int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
+        while (parent != null && count-- > 0) {
+            if (parent instanceof LaunchSourceProvider) {
+                return (LaunchSourceProvider) parent;
+            } else {
+                parent = parent.getParent();
+            }
+        }
+        return null;
+    }
+
+    private String TAG = "UserEvent";
+
+    private long mElapsedContainerMillis;
+    private long mElapsedSessionMillis;
+    private long mActionDurationMillis;
+
+    // Used for filling in predictedRank on {@link Target}s.
+    private List<ComponentKey> mPredictedApps;
+
+    //                      APP_ICON    SHORTCUT    WIDGET
+    // --------------------------------------------------------------
+    // packageNameHash      required    optional    required
+    // componentNameHash    required                required
+    // intentHash                       required
+    // --------------------------------------------------------------
+
+    protected LauncherEvent createLauncherEvent(View v) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(
+                Action.TOUCH, Target.ITEM, Target.CONTAINER);
+        event.action.touch = Action.TAP;
+
+        // Fill in grid(x,y), pageIndex of the child and container type of the parent
+        // TODO: make this percolate up the view hierarchy if needed.
+        int idx = 0;
+        LaunchSourceProvider provider = getLaunchProviderRecursive(v);
+        provider.fillInLaunchSourceData(v, (ItemInfo) v.getTag(), event.srcTarget[idx], event.srcTarget[idx + 1]);
+
+        // TODO: Fill in all the hashes and the predictedRank
+
+        // Fill in the duration of time spent navigating in Launcher and the container.
+        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
+        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
+        return event;
+    }
+
+    public void logAppLaunch(View v, Intent intent) {
+        dispatchUserEvent(createLauncherEvent(v), intent);
+    }
+
+    public void logTap(View v) {
+        // TODO
+    }
+
+    public void logLongPress() {
+        // TODO
+    }
+
+    public void logDragNDrop() {
+        // TODO
+    }
+
+    public void setPredictedApps(List<ComponentKey> predictedApps) {
+        mPredictedApps = predictedApps;
+    }
+
+    /**
+     * Currently logs following containers: workspace, allapps, widget tray.
+     */
+    public final void resetElapsedContainerMillis() {
+        mElapsedContainerMillis = System.currentTimeMillis();
+    }
+
+    public final void resetElapsedSessionMillis() {
+        mElapsedSessionMillis = System.currentTimeMillis();
+        mElapsedContainerMillis = System.currentTimeMillis();
+
+    }
+
+    public final void resetActionDurationMillis() {
+        mActionDurationMillis = System.currentTimeMillis();
+    }
+
+    public abstract void dispatchUserEvent(LauncherEvent ev, Intent intent);
+
+    public int getPredictedRank(ComponentKey key) {
+        if (mPredictedApps == null) return -1;
+        return mPredictedApps.indexOf(key);
+    }
+}
diff --git a/src/com/android/launcher3/logging/UserEventLogger.java b/src/com/android/launcher3/logging/UserEventLogger.java
deleted file mode 100644
index 4e5b2c1..0000000
--- a/src/com/android/launcher3/logging/UserEventLogger.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.android.launcher3.logging;
-
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Stats;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-
-import java.util.Locale;
-
-public abstract class UserEventLogger {
-
-    private String TAG = "UserEventLogger";
-    private boolean DEBUG = false;
-
-    private long mElapsedContainerMillis;
-    private long mElapsedSessionMillis;
-    private long mActionDurationMillis;
-
-
-    public final void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) {
-        if (FeatureFlags.LAUNCHER3_LEGACY_LOGGING) return;
-
-        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
-        event.action = new LauncherLogProto.Action();
-        event.action.type = LauncherLogProto.Action.TOUCH;
-        event.action.touch = LauncherLogProto.Action.TAP;
-
-        event.srcTarget = new LauncherLogProto.Target();
-        event.srcTarget.type = LauncherLogProto.Target.ITEM;
-        event.srcTarget.itemType = LauncherLogProto.APP_ICON;
-        // TODO: package hash name should be different per device.
-        event.srcTarget.packageNameHash = provider.hashCode();
-
-        event.srcTarget.parent = new LauncherLogProto.Target();
-        String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER);
-
-        if (shortcut != null) {
-            event.srcTarget.parent.containerType = LoggerUtils.getContainerType(shortcut);
-            event.srcTarget.pageIndex = (int) shortcut.screenId;
-            event.srcTarget.gridX = shortcut.cellX;
-            event.srcTarget.gridX = shortcut.cellY;
-        }
-        if (subContainer != null) {
-            event.srcTarget.parent.type = LauncherLogProto.Target.CONTAINER;
-            if (subContainer.equals(Stats.SUB_CONTAINER_FOLDER)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.FOLDER;
-            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_A_Z)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.ALLAPPS;
-            } else if (subContainer.equals(Stats.CONTAINER_HOTSEAT)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.HOTSEAT;
-            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_PREDICTION)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.PREDICTION;
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, String.format("parent bundle: %s %s %s %s",
-                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER),
-                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER_PAGE),
-                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER),
-                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE)));
-            }
-        }
-        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
-        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
-        processEvent(event);
-    }
-
-    /**
-     * Currently logs following containers: workspace, allapps, widget tray.
-     */
-    public final void resetElapsedContainerMillis() {
-        mElapsedContainerMillis = System.currentTimeMillis();
-        if(DEBUG) {
-            Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis);
-        }
-    }
-
-    public final void resetElapsedSessionMillis() {
-        mElapsedSessionMillis = System.currentTimeMillis();
-        mElapsedContainerMillis = System.currentTimeMillis();
-        if(DEBUG) {
-            Log.d(TAG, "resetElapsedSessionMillis " + mElapsedSessionMillis);
-        }
-    }
-
-    public final void resetActionDurationMillis() {
-        mActionDurationMillis = System.currentTimeMillis();
-        if(DEBUG) {
-            Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis);
-        }
-    }
-
-    public abstract void processEvent(LauncherLogProto.LauncherEvent ev);
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 931466c..e054734 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -958,13 +958,15 @@
                 // The following list defines all possible grid sizes (and intermediate steps
                 // during migration). Note that at each step, dx <= 1 && dy <= 1. Any grid size
                 // which is not in this list is not migrated.
+                // Note that the InvariantDeviceProfile defines (rows, cols) but the Points
+                // specified here are defined as (cols, rows).
                 ArrayList<Point> gridSizeSteps = new ArrayList<>();
-                gridSizeSteps.add(new Point(2, 3));
+                gridSizeSteps.add(new Point(3, 2));
                 gridSizeSteps.add(new Point(3, 3));
-                gridSizeSteps.add(new Point(3, 4));
+                gridSizeSteps.add(new Point(4, 3));
                 gridSizeSteps.add(new Point(4, 4));
                 gridSizeSteps.add(new Point(5, 5));
-                gridSizeSteps.add(new Point(5, 6));
+                gridSizeSteps.add(new Point(6, 5));
                 gridSizeSteps.add(new Point(6, 6));
                 gridSizeSteps.add(new Point(7, 7));
 
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 1107b44..b2a94bb 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -16,10 +16,10 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -101,7 +101,7 @@
     }
 
     public WidgetsModel updateAndClone(Context context) {
-        Utilities.assertWorkerThread();
+        Preconditions.assertWorkerThread();
 
         try {
             final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index de8da36..8b6f5cd 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -1,6 +1,5 @@
 package com.android.launcher3.testing;
 
-import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -12,9 +11,8 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.logging.UserEventLogger;
 import com.android.launcher3.allapps.AllAppsSearchBarController;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -125,50 +123,18 @@
         }
 
         @Override
-        public void onClickAllAppsButton(View v) {
-        }
-
-        @Override
         public void bindAllApplications(ArrayList<AppInfo> apps) {
         }
 
         @Override
-        public void onClickFolderIcon(View v) {
-        }
-
-        @Override
-        public void onClickAppShortcut(View v) {
-        }
-
-        @Override
-        public void onClickPagedViewIcon(View v) {
-        }
-
-        @Override
-        public void onClickWallpaperPicker(View v) {
-        }
-
-        @Override
         public void onClickSettingsButton(View v) {
         }
 
         @Override
-        public void onClickAddWidgetButton(View v) {
-        }
-
-        @Override
-        public void onPageSwitch(View newPage, int newPageIndex) {
-        }
-
-        @Override
         public void onWorkspaceLockedChanged() {
         }
 
         @Override
-        public void onDragStarted(View view) {
-        }
-
-        @Override
         public void onInteractionBegin() {
         }
 
@@ -187,11 +153,6 @@
             return false;
         }
 
-        @Override
-        public boolean startSearchFromAllApps(String query) {
-            return false;
-        }
-
         CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() {
 
             // Custom content is completely shown. {@code fromResume} indicates whether this was caused
@@ -228,7 +189,7 @@
         }
 
         @Override
-        public UserEventLogger getLogger() { return null; }
+        public UserEventDispatcher getUserEventDispatcher() { return null; }
 
         @Override
         public View getQsbBar() {
@@ -271,11 +232,6 @@
         }
 
         @Override
-        public boolean overrideWallpaperDimensions() {
-            return false;
-        }
-
-        @Override
         public AllAppsSearchBarController getAllAppsSearchBarController() {
             return null;
         }
@@ -292,11 +248,6 @@
         }
 
         @Override
-        public boolean isLauncherPreinstalled() {
-            return false;
-        }
-
-        @Override
         public void setLauncherSearchCallback(Object callbacks) {
             // Do nothing
         }
@@ -307,6 +258,6 @@
 
         @Override
         public void onDetachedFromWindow() {
-        };
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
new file mode 100644
index 0000000..d55d573
--- /dev/null
+++ b/src/com/android/launcher3/util/CachedPackageTracker.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class to track list of installed packages. It persists the list so that apps
+ * installed/uninstalled while Launcher was dead can also be handled properly.
+ */
+public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {
+
+    protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
+
+    protected final SharedPreferences mPrefs;
+    protected final UserManagerCompat mUserManager;
+    protected final LauncherAppsCompat mLauncherApps;
+
+    public CachedPackageTracker(Context context, String preferenceFileName) {
+        mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
+        mUserManager = UserManagerCompat.getInstance(context);
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
+    }
+
+    /**
+     * Checks the list of user apps, and generates package event accordingly.
+     * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
+     */
+    public void processUserApps(List<LauncherActivityInfoCompat> apps, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> oldPackageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
+
+        HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
+        HashSet<String> newPackageSet = new HashSet<>();
+        ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
+
+        for (LauncherActivityInfoCompat info : apps) {
+            String packageName = info.getComponentName().getPackageName();
+            newPackageSet.add(packageName);
+            packagesRemoved.remove(packageName);
+
+            if (!oldPackageSet.contains(packageName)) {
+                oldPackageSet.add(packageName);
+                packagesAdded.add(new LauncherActivityInstallInfo(
+                        info, info.getFirstInstallTime()));
+            }
+        }
+
+        if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
+            mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();
+
+            if (!packagesAdded.isEmpty()) {
+                Collections.sort(packagesAdded);
+                onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
+            }
+
+            if (!packagesRemoved.isEmpty()) {
+                for (String pkg : packagesRemoved) {
+                    onLauncherPackageRemoved(pkg, user);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads the list of user apps which have already been processed.
+     * @return false if the list didn't exist, true otherwise
+     */
+    private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
+        Set<String> userApps = mPrefs.getStringSet(prefKey, null);
+        if (userApps == null) {
+            return false;
+        } else {
+            outExistingApps.addAll(userApps);
+            return true;
+        }
+    }
+
+    @Override
+    public void onPackageRemoved(String packageName, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> packageSet = new HashSet<>();
+        if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
+            mPrefs.edit().putStringSet(prefKey, packageSet).apply();
+        }
+
+        onLauncherPackageRemoved(packageName, user);
+    }
+
+    @Override
+    public void onPackageAdded(String packageName, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> packageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(packageSet, prefKey);
+        if (!packageSet.contains(packageName)) {
+            List<LauncherActivityInfoCompat> activities =
+                    mLauncherApps.getActivityList(packageName, user);
+            if (!activities.isEmpty()) {
+                LauncherActivityInfoCompat activityInfo = activities.get(0);
+
+                packageSet.add(packageName);
+                mPrefs.edit().putStringSet(prefKey, packageSet).apply();
+                onLauncherAppsAdded(Arrays.asList(
+                        new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
+                        user, userAppsExisted);
+            }
+        }
+    }
+
+    @Override
+    public void onPackageChanged(String packageName, UserHandleCompat user) { }
+
+    @Override
+    public void onPackagesAvailable(
+            String[] packageNames, UserHandleCompat user, boolean replacing) { }
+
+    @Override
+    public void onPackagesUnavailable(
+            String[] packageNames, UserHandleCompat user, boolean replacing) { }
+
+    @Override
+    public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) { }
+
+    @Override
+    public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) { }
+
+    /**
+     * Called when new launcher apps are added.
+     * @param apps list of newly added activities. Only one entry per package is sent.
+     * @param user the user for this event. All activities in {@param apps} will belong to
+     *             the same user.
+     * @param userAppsExisted false if the list was processed for the first time, like in case
+     *                        when Launcher was newly installed or a new user was added.
+     */
+    protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
+            UserHandleCompat user, boolean userAppsExisted);
+
+    /**
+     * Called when apps are removed from the system.
+     */
+    protected abstract void onLauncherPackageRemoved(String packageName, UserHandleCompat user);
+
+    protected static class LauncherActivityInstallInfo
+            implements Comparable<LauncherActivityInstallInfo> {
+        public final LauncherActivityInfoCompat info;
+        public final long installTime;
+
+        public LauncherActivityInstallInfo(LauncherActivityInfoCompat info, long installTime) {
+            this.info = info;
+            this.installTime = installTime;
+        }
+
+        @Override
+        public int compareTo(LauncherActivityInstallInfo another) {
+            return Utilities.longCompare(installTime, another.installTime);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/IconNormalizer.java b/src/com/android/launcher3/util/IconNormalizer.java
index 001cac0..4087d7b 100644
--- a/src/com/android/launcher3/util/IconNormalizer.java
+++ b/src/com/android/launcher3/util/IconNormalizer.java
@@ -28,7 +28,7 @@
 public class IconNormalizer {
 
     // Ratio of icon visible area to full icon size for a square shaped icon
-    private static final float MAX_SQUARE_AREA_FACTOR = 359.0f / 576;
+    private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576;
     // Ratio of icon visible area to full icon size for a circular shaped icon
     private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
 
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 3925c40..df23abe 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -16,14 +16,8 @@
 
 package com.android.launcher3.util;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.util.Log;
 
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
@@ -35,27 +29,19 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Handles addition of app shortcuts for managed profiles.
  * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ManagedProfileHeuristic {
 
-    private static final String TAG = "ManagedProfileHeuristic";
-
     /**
      * Maintain a set of packages installed per user.
      */
@@ -76,228 +62,137 @@
     }
 
     private final Context mContext;
-    private final UserHandleCompat mUser;
     private final LauncherModel mModel;
-
-    private final SharedPreferences mPrefs;
-    private final long mUserSerial;
-    private final long mUserCreationTime;
-    private final String mPackageSetKey;
-
-    private ArrayList<ShortcutInfo> mHomescreenApps;
-    private ArrayList<ShortcutInfo> mWorkFolderApps;
-    private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
+    private final UserHandleCompat mUser;
 
     private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
         mContext = context;
         mUser = user;
         mModel = LauncherAppState.getInstance().getModel();
-
-        UserManagerCompat userManager = UserManagerCompat.getInstance(context);
-        mUserSerial = userManager.getSerialNumberForUser(user);
-        mUserCreationTime = userManager.getUserCreationTime(user);
-        mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial;
-
-        mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
-                Context.MODE_PRIVATE);
     }
 
-    private void initVars() {
-        mHomescreenApps = new ArrayList<>();
-        mWorkFolderApps = new ArrayList<>();
-        mShortcutToInstallTimeMap = new HashMap<>();
+    public void processPackageRemoved(String[] packages) {
+        Preconditions.assertWorkerThread();
+        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
+        for (String pkg : packages) {
+            handler.onPackageRemoved(pkg, mUser);
+        }
     }
 
-    /**
-     * Checks the list of user apps and adds icons for newly installed apps on the homescreen or
-     * workfolder.
-     */
+    public void processPackageAdd(String[] packages) {
+        Preconditions.assertWorkerThread();
+        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
+        for (String pkg : packages) {
+            handler.onPackageAdded(pkg, mUser);
+        }
+    }
+
     public void processUserApps(List<LauncherActivityInfoCompat> apps) {
-        initVars();
+        Preconditions.assertWorkerThread();
+        new ManagedProfilePackageHandler().processUserApps(apps, mUser);
+    }
 
-        HashSet<String> packageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(packageSet);
+    private class ManagedProfilePackageHandler extends CachedPackageTracker {
 
-        boolean newPackageAdded = false;
-        for (LauncherActivityInfoCompat info : apps) {
-            String packageName = info.getComponentName().getPackageName();
-            if (!packageSet.contains(packageName)) {
-                packageSet.add(packageName);
-                newPackageAdded = true;
-                markForAddition(info, info.getFirstInstallTime());
-            }
+        private ManagedProfilePackageHandler() {
+            super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY);
         }
 
-        if (newPackageAdded) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
+        protected void onLauncherAppsAdded(
+                List<LauncherActivityInstallInfo> apps, UserHandleCompat user, boolean userAppsExisted) {
+            ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>();
+            ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>();
+
+            int count = apps.size();
+            long folderCreationTime =
+                    mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+
+            for (int i = 0; i < count; i++) {
+                LauncherActivityInstallInfo info = apps.get(i);
+
+                ShortcutInfo si = ShortcutInfo.fromActivityInfo(info.info, mContext);
+                ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
+            }
+
+            finalizeWorkFolder(user, workFolderApps, homescreenApps);
+
             // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
             // getting filled with the managed user apps, when it start with a fresh DB (or after
             // a very long time).
-            finalizeAdditions(userAppsExisted);
-        }
-    }
-
-    private void markForAddition(LauncherActivityInfoCompat info, long installTime) {
-        ArrayList<ShortcutInfo> targetList =
-                (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
-                        mWorkFolderApps : mHomescreenApps;
-        ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
-        mShortcutToInstallTimeMap.put(si, installTime);
-        targetList.add(si);
-    }
-
-    private void sortList(ArrayList<ShortcutInfo> infos) {
-        Collections.sort(infos, new Comparator<ShortcutInfo>() {
-
-            @Override
-            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
-                Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
-                Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
-                return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
-                        rhsTime == null ? 0 : rhsTime);
+            if (userAppsExisted && !homescreenApps.isEmpty()) {
+                mModel.addAndBindAddedWorkspaceItems(mContext, homescreenApps);
             }
-        });
-    }
-
-    /**
-     * Adds and binds shortcuts marked to be added to the work folder.
-     */
-    private void finalizeWorkFolder() {
-        if (mWorkFolderApps.isEmpty()) {
-            return;
         }
-        sortList(mWorkFolderApps);
 
-        // Try to get a work folder.
-        String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
-        if (mPrefs.contains(folderIdKey)) {
-            long folderId = mPrefs.getLong(folderIdKey, 0);
-            final FolderInfo workFolder = mModel.findFolderById(folderId);
+        @Override
+        protected void onLauncherPackageRemoved(String packageName, UserHandleCompat user) {
+        }
 
-            if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
-                // Could not get a work folder. Add all the icons to homescreen.
-                mHomescreenApps.addAll(mWorkFolderApps);
+        /**
+         * Adds and binds shortcuts marked to be added to the work folder.
+         */
+        private void finalizeWorkFolder(
+                UserHandleCompat user, final ArrayList<ShortcutInfo> workFolderApps,
+                ArrayList<ShortcutInfo> homescreenApps) {
+            if (workFolderApps.isEmpty()) {
                 return;
             }
-            saveWorkFolderShortcuts(folderId, workFolder.contents.size());
+            // Try to get a work folder.
+            String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user);
+            if (mPrefs.contains(folderIdKey)) {
+                long folderId = mPrefs.getLong(folderIdKey, 0);
+                final FolderInfo workFolder = mModel.findFolderById(folderId);
 
-            final ArrayList<ShortcutInfo> shortcuts = mWorkFolderApps;
-            // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
-            new MainThreadExecutor().execute(new Runnable() {
-
-                @Override
-                public void run() {
-                    for (ShortcutInfo info : shortcuts) {
-                        workFolder.add(info, false);
-                    }
+                if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
+                    // Could not get a work folder. Add all the icons to homescreen.
+                    homescreenApps.addAll(0, workFolderApps);
+                    return;
                 }
-            });
-        } else {
-            // Create a new folder.
-            final FolderInfo workFolder = new FolderInfo();
-            workFolder.title = mContext.getText(R.string.work_folder_name);
-            workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+                saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps);
 
-            // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
-            for (ShortcutInfo info : mWorkFolderApps) {
-                workFolder.add(info, false);
+                // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
+                new MainThreadExecutor().execute(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        for (ShortcutInfo info : workFolderApps) {
+                            workFolder.add(info, false);
+                        }
+                    }
+                });
+            } else {
+                // Create a new folder.
+                final FolderInfo workFolder = new FolderInfo();
+                workFolder.title = mContext.getText(R.string.work_folder_name);
+                workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+
+                // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
+                for (ShortcutInfo info : workFolderApps) {
+                    workFolder.add(info, false);
+                }
+
+                // Add the item to home screen and DB. This also generates an item id synchronously.
+                ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
+                itemList.add(workFolder);
+                mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
+                mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();
+
+                saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
             }
-
-            // Add the item to home screen and DB. This also generates an item id synchronously.
-            ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
-            itemList.add(workFolder);
-            mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
-            mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply();
-
-            saveWorkFolderShortcuts(workFolder.id, 0);
         }
     }
 
     /**
      * Add work folder shortcuts to the DB.
      */
-    private void saveWorkFolderShortcuts(long workFolderId, int startingRank) {
-        for (ItemInfo info : mWorkFolderApps) {
+    private void saveWorkFolderShortcuts(
+            long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
+        for (ItemInfo info : workFolderApps) {
             info.rank = startingRank++;
             LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
         }
     }
 
-    /**
-     * Adds and binds all shortcuts marked for addition.
-     */
-    private void finalizeAdditions(boolean addHomeScreenShortcuts) {
-        finalizeWorkFolder();
-
-        if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
-            sortList(mHomescreenApps);
-            mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
-        }
-    }
-
-    /**
-     * Updates the list of installed apps and adds any new icons on homescreen or work folder.
-     */
-    public void processPackageAdd(String[] packages) {
-        initVars();
-        HashSet<String> packageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(packageSet);
-
-        boolean newPackageAdded = false;
-        long installTime = System.currentTimeMillis();
-        LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
-
-        for (String packageName : packages) {
-            if (!packageSet.contains(packageName)) {
-                packageSet.add(packageName);
-                newPackageAdded = true;
-
-                List<LauncherActivityInfoCompat> activities =
-                        launcherApps.getActivityList(packageName, mUser);
-                if (!activities.isEmpty()) {
-                    markForAddition(activities.get(0), installTime);
-                }
-            }
-        }
-
-        if (newPackageAdded) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-            finalizeAdditions(userAppsExisted);
-        }
-    }
-
-    /**
-     * Updates the list of installed packages for the user.
-     */
-    public void processPackageRemoved(String[] packages) {
-        HashSet<String> packageSet = new HashSet<String>();
-        getUserApps(packageSet);
-        boolean packageRemoved = false;
-
-        for (String packageName : packages) {
-            if (packageSet.remove(packageName)) {
-                packageRemoved = true;
-            }
-        }
-
-        if (packageRemoved) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-        }
-    }
-
-    /**
-     * Reads the list of user apps which have already been processed.
-     * @return false if the list didn't exist, true otherwise
-     */
-    private boolean getUserApps(HashSet<String> outExistingApps) {
-        Set<String> userApps = mPrefs.getStringSet(mPackageSetKey, null);
-        if (userApps == null) {
-            return false;
-        } else {
-            outExistingApps.addAll(userApps);
-            return true;
-        }
-    }
 
     /**
      * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
new file mode 100644
index 0000000..08e8e86
--- /dev/null
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility methods using package manager
+ */
+public class PackageManagerHelper {
+
+    private static final int FLAG_SUSPENDED = 1<<30;
+
+    /**
+     * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
+     * guarantee that the app is on SD card.
+     */
+    public static boolean isAppOnSdcard(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, 0);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
+            return info != null && info.enabled;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(PackageManager pm, String packageName) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return info != null && isAppSuspended(info);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(ApplicationInfo info) {
+        // The value of FLAG_SUSPENDED was reused by a hidden constant
+        // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
+        // or later.
+        if (Utilities.isNycOrAbove()) {
+            return (info.flags & FLAG_SUSPENDED) != 0;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
new file mode 100644
index 0000000..3760c63
--- /dev/null
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.os.Looper;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.ProviderConfig;
+
+/**
+ * A set of utility methods for thread verification.
+ */
+public class Preconditions {
+
+    public static void assertWorkerThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    public static void assertUIThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    public static void assertNonUiThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private static boolean isSameLooper(Looper looper) {
+        return Looper.myLooper() == looper;
+    }
+}