am 23b816b1: (-s ours) Import translations. DO NOT MERGE

* commit '23b816b1bafee73eee28caca3e2a2915f726bbd9':
  Import translations. DO NOT MERGE
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 561c4bb..11684c3 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -141,7 +141,21 @@
         final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
             protected Void doInBackground(Void...args) {
                 if (!isCancelled()) {
-                    bitmapSource.loadInBackground();
+                    try {
+                        bitmapSource.loadInBackground();
+                    } catch (SecurityException securityException) {
+                        if (isDestroyed()) {
+                            // Temporarily granted permissions are revoked when the activity
+                            // finishes, potentially resulting in a SecurityException here.
+                            // Even though {@link #isDestroyed} might also return true in different
+                            // situations where the configuration changes, we are fine with
+                            // catching these cases here as well.
+                            cancel(false);
+                        } else {
+                            // otherwise it had a different cause and we throw it further
+                            throw securityException;
+                        }
+                    }
                 }
                 return null;
             }
@@ -352,10 +366,13 @@
                 getWindowManager());
         // Get the crop
         RectF cropRect = mCropView.getCrop();
+
+        Point inSize = mCropView.getSourceDimensions();
+
         int cropRotation = mCropView.getImageRotation();
         float cropScale = mCropView.getWidth() / (float) cropRect.width();
 
-        Point inSize = mCropView.getSourceDimensions();
+
         Matrix rotateMatrix = new Matrix();
         rotateMatrix.setRotate(cropRotation);
         float[] rotatedInSize = new float[] { inSize.x, inSize.y };
@@ -363,6 +380,14 @@
         rotatedInSize[0] = Math.abs(rotatedInSize[0]);
         rotatedInSize[1] = Math.abs(rotatedInSize[1]);
 
+
+        // due to rounding errors in the cropview renderer the edges can be slightly offset
+        // therefore we ensure that the boundaries are sanely defined
+        cropRect.left = Math.max(0, cropRect.left);
+        cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
+        cropRect.top = Math.max(0, cropRect.top);
+        cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
+
         // ADJUST CROP WIDTH
         // Extend the crop all the way to the right, for parallax
         // (or all the way to the left, in RTL)
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index b5c17c5..3d337db 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Wag tans…"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Laai tans af…"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installeer tans…"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nie teruggestel nie"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index ce9fb9b..489164c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"በመጠበቅ ላይ"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"በማውረድ ላይ"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"በመጫን ላይ"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"ወደነበረበት አልተመለሰም"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 7eb37d3..90eae74 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"انتظار"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"جارٍ التنزيل"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"جارٍ التثبيت"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"استعادة مخفقة"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 13c0118..198cd18 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Изчаква"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Изтегля се"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Инсталира се"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Не е възстановено"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 3e6bedb..c187094 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"En espera"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"S\'està baixant"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instal·lant"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"No restaurat"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 3a0e672..6870f89 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Čekání"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Stahování"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalace"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nebylo obnoveno"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index eabde3e..5d8b385 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Afventer"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Downloader"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installerer"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ikke gendannet"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 27ad73a..6b09789 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Warten"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Download läuft"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installation"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nicht wiederhergestellt"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ec05ae5..66cea00 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Αναμονή"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Λήψη "</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Εγκατάσταση"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Δεν ανακτήθηκε"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index cab1707..eed1f80 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Waiting"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Downloading"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installing"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Not restored"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index cab1707..eed1f80 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Waiting"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Downloading"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installing"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Not restored"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 278f0d8..b325c35 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Pendiente"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Descargando"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"No restaurado"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index f9cf809..3efdf9b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Esperando"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Descargando"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"No restaurado"</string>
 </resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 92985d2..73e4779 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Ootamine"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Allalaadimine"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installimine"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ei taastatud"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a01ea79..d5e271b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"در حال انتظار"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"در حال دانلود"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"در حال نصب"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"بازیابی نشد"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9a48ab3..c6ffdf6 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Odottaa"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Ladataan"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Asennetaan"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ei palautettu"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 92c5cc4..0c71823 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"En attente"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Téléchargement..."</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installation…"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Non restauré"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 18732f8..41ea546 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"En attente"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Téléchargement…"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installation…"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Non restauré"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b889625..9bbb5cb 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"प्रतीक्षा में"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"डाउनलोड हो रहा है"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"इंस्टॉल हो रहा है"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"पुन:स्थापित नहीं हुआ"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 32f3feb..8116ba4 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -107,4 +107,9 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+    <string name="package_state_enqueued" msgid="6227252464303085641">"Čekanje"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Preuzimanje"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instaliranje"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nije vraćeno"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 37d5059..59fc6e1 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Várakozik"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Letöltés alatt"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Települ"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nincs visszaállítva"</string>
 </resources>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 6e9491b..d02d48e 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Առկախ է"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Ներբեռնվում է"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Տեղադրվում է"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Չի վերականգնվել"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index d5df63b..ba85886 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Menunggu"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Mengunduh"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Memasang"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Tak dipulihkan"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 20451d1..0d3bcc4 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"In attesa"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Download..."</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installazione..."</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Non ripristinato"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 08d9653..cdfaf04 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"ממתין"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"מוריד"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"מתקין"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"לא שוחזרה"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ad5e8ff..e3ec47d 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"待機中"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"ダウンロード中"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"インストール中"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"復元失敗"</string>
 </resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 086df72..de701ca 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"მოცდა..."</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"ჩამოტვირთვა..."</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"ინსტალაცია..."</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"არ აღდგა"</string>
 </resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 8bf26c2..7e6b799 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"រង់ចាំ"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"​ទាញ​យក"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"ដំឡើង"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"មិនបាន​​ស្តា​រ"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index dde4eb7..f8a06c7 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"대기 중"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"다운로드 중"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"설치 중"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"복원되지 않음"</string>
 </resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 50b5c08..a4fc842 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"ກຳ​ລັງ​ລໍ​ຖ້າ"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"ກຳລັງດາວໂຫລດ"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"​ກຳ​ລັງ​ຕິດ​ຕັ້ງ"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index aba231d..471201d 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Laukiama"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Atsisiunčiama"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Diegiama"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Neatkurta"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 6d4888d..6fb7d60 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Gaida"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Lejupielādē"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalē"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nav atjaunota"</string>
 </resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index db92a5c..a8ad568 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Хүлээж байна"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Татаж авч байна"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Суулгаж байна"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Сэргээгээгүй"</string>
 </resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 678eeb4..4c4b95f 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Menunggu"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Memuat turun"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Memasang"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Tak dipulihkan"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index dd89a4f..34cc488 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Venter …"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Laster ned …"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installerer …"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ikke gjenoppr."</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index d39c1ea..7bd3025 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Wachten"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Downloaden"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installeren"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Niet hersteld"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b428a95..07d7465 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Oczekiwanie"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Pobieranie"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalowanie"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nie przywrócono"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b8e51d6..0717c52 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"A aguardar"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"A transferir "</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"A instalar"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Não restaurado"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 03c621a..e3f60ae 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Aguardando"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Transferindo"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Não restaurado"</string>
 </resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 3600095..be35c6b 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -192,4 +192,14 @@
     <skip />
     <!-- no translation found for settings_button_text (8119458837558863227) -->
     <skip />
+    <!-- no translation found for package_state_enqueued (6227252464303085641) -->
+    <skip />
+    <!-- no translation found for package_state_downloading (4088770468458724721) -->
+    <skip />
+    <!-- no translation found for package_state_installing (7588193972189849870) -->
+    <skip />
+    <!-- no translation found for package_state_unknown (7592128424511031410) -->
+    <skip />
+    <!-- no translation found for package_state_error (7672093962724223588) -->
+    <skip />
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index a667ffd..48fa11e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"În așteptare"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Se descarcă"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Se instalează"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nerestabilit"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index c2e8bc7..8303350 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Ожидается"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Скачивается"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Устанавливается"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Не восстановлен"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index acd8e05..b274926 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Čaká sa"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Sťahovanie"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Inštalácia"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Nebolo obnovené"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index afbd620..62d90bb 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Čakanje"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Prenašanje"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Nameščanje"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ni obnovljen"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bf71b28..9b30913 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Чека се"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Преузима се"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Инсталира се"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Није враћено"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 5825eec..0469a8a 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Väntar"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Hämtas"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Installerar"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Inte återställt"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index d2a282a..81126fd 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -109,4 +109,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Inasubiri"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Inapakua"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Inasakinisha"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Haijarejeshwa"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index beaa0ae..1d69750 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"กำลังรอ"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"กำลังดาวน์โหลด"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"กำลังติดตั้ง"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"ไม่ได้คืนค่า"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 5be9d85..d30fb11 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Naghihintay"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Nagda-download"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Nag-i-install"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Hindi naibalik"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 01f79a2..bdc97df 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Bekliyor"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"İndiriliyor"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Yükleniyor"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Geri yüklenmedi"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index f266eea..94df4f3 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Очікування"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Завантаження"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Встановлення"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Не відновлено"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 9d4ed41..434b556 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Đang đợi"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Đang tải xuống"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Đang cài đặt"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Không được khôi phục"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3a2638e..a771f47 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"正在等待"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"正在下载"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"正在安装"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"无法还原"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index cad95f2..ed19b44 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"等候中"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"下載中"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"安裝中"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"無法還原"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 5f48243..2449f60 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"等待中"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"下載中…"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"安裝中"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"無法還原"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e982eb4..209816e 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -107,4 +107,9 @@
     <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="package_state_enqueued" msgid="6227252464303085641">"Ilindile"</string>
+    <string name="package_state_downloading" msgid="4088770468458724721">"Iyalanda"</string>
+    <string name="package_state_installing" msgid="7588193972189849870">"Iyafaka"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
+    <string name="package_state_error" msgid="7672093962724223588">"Ayibuyiselwe"</string>
 </resources>
diff --git a/res/values/integers.xml b/res/values/integers.xml
new file mode 100644
index 0000000..7d26d85
--- /dev/null
+++ b/res/values/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+    <integer name="promise_icon_alpha">127</integer>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9cb6c29..ad3a1c4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -20,6 +20,10 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- General -->
     <skip />
+
+    <!-- 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>
+
     <!-- Application name -->
     <string name="application_name">Launcher3</string>
     <!-- Accessibility-facing application name -->
@@ -262,4 +266,15 @@
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for settings button -->
     <string name="settings_button_text">Settings</string>
+
+    <!-- Label on an icon that references an uninstalled package, that is going to be installed at some point. [CHAR_LIMIT=15] -->
+    <string name="package_state_enqueued">Waiting</string>
+    <!-- Label on an icon that references an uninstalled package, that is currently being downloaded. [CHAR_LIMIT=15] -->
+    <string name="package_state_downloading">Downloading</string>
+    <!-- Label on an icon that references an uninstalled package, that is currently being installed. [CHAR_LIMIT=15] -->
+    <string name="package_state_installing">Installing</string>
+    <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+    <string name="package_state_unknown">Unknown</string>
+    <!-- Label on an icon that references an uninstalled package, for which restore from market has failed. [CHAR_LIMIT=15] -->
+    <string name="package_state_error">Not restored</string>
 </resources>
diff --git a/res/xml-sw600dp/default_workspace.xml b/res/xml-sw600dp/default_workspace.xml
index 090c7a7..d42a93a 100644
--- a/res/xml-sw600dp/default_workspace.xml
+++ b/res/xml-sw600dp/default_workspace.xml
@@ -60,13 +60,21 @@
     <!-- Far-right screen [4] -->
 
     <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <favorite
-        launcher:packageName="com.android.dialer"
-        launcher:className="com.android.dialer.DialtactsActivity"
+    <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+    <resolve
         launcher:container="-101"
         launcher:screen="1"
         launcher:x="1"
-        launcher:y="0" />
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+        <favorite launcher:uri="tel:123" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+
+        <favorite
+            launcher:packageName="com.android.dialer"
+            launcher:className="com.android.dialer.DialtactsActivity" />
+    </resolve>
+
     <favorite
         launcher:packageName="com.android.contacts"
         launcher:className="com.android.contacts.activities.PeopleActivity"
@@ -74,18 +82,34 @@
         launcher:screen="2"
         launcher:x="2"
         launcher:y="0" />
-    <favorite
-        launcher:packageName="com.android.mms"
-        launcher:className="com.android.mms.ui.ConversationList"
+
+    <resolve
         launcher:container="-101"
         launcher:screen="4"
         launcher:x="4"
-        launcher:y="0" />
-    <favorite
-        launcher:packageName="com.android.browser"
-        launcher:className="com.android.browser.BrowserActivity"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+
+        <favorite
+            launcher:packageName="com.android.mms"
+            launcher:className="com.android.mms.ui.ConversationList" />
+    </resolve>
+    <resolve
         launcher:container="-101"
         launcher:screen="5"
         launcher:x="5"
-        launcher:y="0" />
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+
+        <favorite
+            launcher:packageName="com.android.browser"
+            launcher:className="com.android.browser.BrowserActivity" />
+    </resolve>
 </favorites>
diff --git a/res/xml/default_workspace.xml b/res/xml/default_workspace.xml
index 26fc504..9bec86a 100644
--- a/res/xml/default_workspace.xml
+++ b/res/xml/default_workspace.xml
@@ -60,13 +60,21 @@
     <!-- Far-right screen [4] -->
 
     <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <favorite
-        launcher:packageName="com.android.dialer"
-        launcher:className="com.android.dialer.DialtactsActivity"
+    <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+    <resolve
         launcher:container="-101"
         launcher:screen="0"
         launcher:x="0"
-        launcher:y="0" />
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+        <favorite launcher:uri="tel:123" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+
+        <favorite
+            launcher:packageName="com.android.dialer"
+            launcher:className="com.android.dialer.DialtactsActivity" />
+    </resolve>
+
     <favorite
         launcher:packageName="com.android.contacts"
         launcher:className="com.android.contacts.activities.PeopleActivity"
@@ -74,18 +82,35 @@
         launcher:screen="1"
         launcher:x="1"
         launcher:y="0" />
-    <favorite
-        launcher:packageName="com.android.mms"
-        launcher:className="com.android.mms.ui.ConversationList"
+
+    <resolve
         launcher:container="-101"
         launcher:screen="3"
         launcher:x="3"
-        launcher:y="0" />
-    <favorite
-        launcher:packageName="com.android.browser"
-        launcher:className="com.android.browser.BrowserActivity"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+
+        <favorite
+            launcher:packageName="com.android.mms"
+            launcher:className="com.android.mms.ui.ConversationList" />
+    </resolve>
+    <resolve
         launcher:container="-101"
         launcher:screen="4"
         launcher:x="4"
-        launcher:y="0" />
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+
+        <favorite
+            launcher:packageName="com.android.browser"
+            launcher:className="com.android.browser.BrowserActivity" />
+    </resolve>
+
 </favorites>
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index da222f1..f85f691 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -31,7 +31,7 @@
 /**
  * Represents an app in AllAppsView.
  */
-class AppInfo extends ItemInfo {
+public class AppInfo extends ItemInfo {
     private static final String TAG = "Launcher3.AppInfo";
 
     /**
@@ -60,7 +60,7 @@
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
 
-    protected Intent getIntent() {
+    public Intent getIntent() {
         return intent;
     }
 
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 251ae21..d6e0bb4 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -225,17 +225,6 @@
 
     private Rect mTmpRect = new Rect();
 
-    // Used for drawing shortcut previews
-    BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
-    PaintCache mCachedShortcutPreviewPaint = new PaintCache();
-    CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
-
-    // Used for drawing widget previews
-    CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
-    RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
-    RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
-    PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
-
     WidgetPreviewLoader mWidgetPreviewLoader;
 
     private boolean mInBulkBind;
@@ -511,8 +500,7 @@
             if (mPressedIcon != null) {
                 mPressedIcon.lockDrawableState();
             }
-            mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
-            mLauncher.getStats().recordLaunch(appInfo.intent);
+            mLauncher.onClickPagedViewIcon(v, appInfo);
         } else if (v instanceof PagedViewWidget) {
             // Let the user know that they have to long press to add a widget
             if (mWidgetInstructionToast != null) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ee42904..c180d32 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -25,6 +25,7 @@
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.widget.TextView;
@@ -43,6 +44,10 @@
     static final float PADDING_H = 8.0f;
     static final float PADDING_V = 3.0f;
 
+    private static final String TAG = "BubbleTextView";
+
+    private static final boolean DEBUG = false;
+
     private int mPrevAlpha = -1;
 
     private HolographicOutlineHelper mOutlineHelper;
@@ -64,6 +69,11 @@
 
     private boolean mStayPressed;
     private CheckLongPressHelper mLongPressHelper;
+    private int mInstallState;
+
+    private int mState;
+
+    private CharSequence mDefaultText = "";
 
     public BubbleTextView(Context context) {
         super(context);
@@ -108,11 +118,14 @@
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
-        setCompoundDrawables(null,
-                Utilities.createIconDrawable(b), null, null);
+        Drawable iconDrawable = Utilities.createIconDrawable(b);
+        setCompoundDrawables(null, iconDrawable, null, null);
         setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
         setText(info.title);
         setTag(info);
+        if (info.isPromise()) {
+            setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN); // TODO: persist this state somewhere
+        }
     }
 
     @Override
@@ -392,4 +405,52 @@
 
         mLongPressHelper.cancelLongPress();
     }
+
+    public void setState(int state) {
+        if (mState == ShortcutInfo.PACKAGE_STATE_DEFAULT && mState != state) {
+            mDefaultText = getText();
+        }
+        mState = state;
+        applyState();
+    }
+
+    private void applyState() {
+        int alpha = getResources().getInteger(R.integer.promise_icon_alpha);
+        if (DEBUG) Log.d(TAG, "applying icon state: " + mState);
+
+        switch(mState) {
+            case ShortcutInfo.PACKAGE_STATE_DEFAULT:
+                super.setText(mDefaultText);
+                alpha = 255;
+                break;
+
+            case ShortcutInfo.PACKAGE_STATE_ENQUEUED:
+                setText(R.string.package_state_enqueued);
+                break;
+
+            case ShortcutInfo.PACKAGE_STATE_DOWNLOADING:
+                setText(R.string.package_state_downloading);
+                break;
+
+            case ShortcutInfo.PACKAGE_STATE_INSTALLING:
+                setText(R.string.package_state_installing);
+                break;
+
+            case ShortcutInfo.PACKAGE_STATE_ERROR:
+                setText(R.string.package_state_error);
+                break;
+
+            case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
+            default:
+                setText(R.string.package_state_unknown);
+                break;
+        }
+        if (DEBUG) Log.d(TAG, "setting icon alpha to: " + alpha);
+        Drawable[] drawables = getCompoundDrawables();
+        for (int i = 0; i < drawables.length; i++) {
+            if (drawables[i] != null) {
+                drawables[i].setAlpha(alpha);
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 862ceca..c54db01 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -73,7 +73,9 @@
 
     private final Rect mInsets = new Rect();
 
-    private int mDragViewIndex;
+    private View mOverlayView;
+    private int mTopViewIndex;
+    private int mChildCountOnLastUpdate = -1;
 
     /**
      * Used to create a new DragLayer from XML.
@@ -120,6 +122,20 @@
         setInsets(child, mInsets, new Rect());
     }
 
+    public void showOverlayView(View overlayView) {
+        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+        mOverlayView = overlayView;
+        addView(overlayView, lp);
+
+        // ensure that the overlay view stays on top. we can't use drawing order for this
+        // because in API level 16 touch dispatch doesn't respect drawing order.
+        mOverlayView.bringToFront();
+    }
+
+    public void dismissOverlayView() {
+        removeView(mOverlayView);
+    }
+
     private void setInsets(View child, Rect newInsets, Rect oldInsets) {
         final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
         if (child instanceof Insettable) {
@@ -762,6 +778,11 @@
 
     @Override
     public void onChildViewAdded(View parent, View child) {
+        if (mOverlayView != null) {
+            // ensure that the overlay view stays on top. we can't use drawing order for this
+            // because in API level 16 touch dispatch doesn't respect drawing order.
+            mOverlayView.bringToFront();
+        }
         updateChildIndices();
     }
 
@@ -770,27 +791,51 @@
         updateChildIndices();
     }
 
+    @Override
+    public void bringChildToFront(View child) {
+        super.bringChildToFront(child);
+        if (child != mOverlayView && mOverlayView != null) {
+            // ensure that the overlay view stays on top. we can't use drawing order for this
+            // because in API level 16 touch dispatch doesn't respect drawing order.
+            mOverlayView.bringToFront();
+        }
+        updateChildIndices();
+    }
+
     private void updateChildIndices() {
-        mDragViewIndex = -1;
+        mTopViewIndex = -1;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             if (getChildAt(i) instanceof DragView) {
-                mDragViewIndex = i;
+                mTopViewIndex = i;
             }
         }
+        mChildCountOnLastUpdate = childCount;
     }
 
     @Override
     protected int getChildDrawingOrder(int childCount, int i) {
-        if (mDragViewIndex == -1) {
+        if (mChildCountOnLastUpdate != childCount) {
+            // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
+            // Pre-18, the child was not added / removed by the time of those callbacks. We need to
+            // force update our representation of things here to avoid crashing on pre-18 devices
+            // in certain instances.
+            updateChildIndices();
+        }
+
+        // i represents the current draw iteration
+        if (mTopViewIndex == -1) {
+            // in general we do nothing
             return i;
-        } else if (i == mDragViewIndex) {
-            return getChildCount()-1;
-        } else if (i < mDragViewIndex) {
+        } else if (i == childCount - 1) {
+            // if we have a top index, we return it when drawing last item (highest z-order)
+            return mTopViewIndex;
+        } else if (i < mTopViewIndex) {
             return i;
         } else {
-            // i > mDragViewIndex
-            return i-1;
+            // for indexes greater than the top index, we fetch one item above to shift for the
+            // displacement of the top index
+            return i + 1;
         }
     }
 
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index b4c3992..fb226e5 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -303,6 +303,10 @@
         return mFolderName;
     }
 
+    public CellLayout getContent() {
+        return mContent;
+    }
+
     /**
      * We need to handle touch events to prevent them from falling through to the workspace below.
      */
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 78026f1..71a7461 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -308,7 +308,7 @@
         }
     }
 
-    Folder getFolder() {
+    public Folder getFolder() {
         return mFolder;
     }
 
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index d45e4e4..42e2ec3 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -23,7 +23,7 @@
 /**
  * Represents a folder containing shortcuts or apps.
  */
-class FolderInfo extends ItemInfo {
+public class FolderInfo extends ItemInfo {
 
     /**
      * Whether this folder has been opened
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 59d60e3..2ac2f00 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -156,15 +156,9 @@
             allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
             if (mLauncher != null) {
                 allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+                mLauncher.setAllAppsButton(allAppsButton);
+                allAppsButton.setOnClickListener(mLauncher);
             }
-            allAppsButton.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(android.view.View v) {
-                    if (mLauncher != null) {
-                        mLauncher.onClickAllAppsButton(v);
-                    }
-                }
-            });
 
             // Note: We do this to ensure that the hotseat is always laid out in the orientation of
             // the hotseat in order regardless of which orientation they were added
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 827718b..ee9f4d4 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -387,20 +387,6 @@
             }
         }
 
-        if (icon != null) {
-            // TODO: handle alpha mask in the view layer
-            Bitmap b = Bitmap.createBitmap(Math.max(icon.getWidth(), 1),
-                    Math.max(icon.getHeight(), 1),
-                    Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(b);
-            Paint paint = new Paint();
-            paint.setAlpha(127);
-            c.drawBitmap(icon, 0, 0, paint);
-            c.setBitmap(null);
-            icon.recycle();
-            icon = b;
-        }
-
         return icon;
     }
 
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 3dc92c9..3fe4c60 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -34,7 +34,7 @@
     /**
      * The id in the settings database for this item
      */
-    long id = NO_ID;
+    public long id = NO_ID;
     
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
@@ -42,7 +42,7 @@
      * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER}, or
      * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
      */
-    int itemType;
+    public int itemType;
     
     /**
      * The id of the container that holds this item. For the desktop, this will be 
@@ -50,27 +50,27 @@
      * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
      * it will be the id of the folder.
      */
-    long container = NO_ID;
+    public long container = NO_ID;
     
     /**
      * Iindicates the screen in which the shortcut appears.
      */
-    long screenId = -1;
+    public long screenId = -1;
     
     /**
      * Indicates the X position of the associated cell.
      */
-    int cellX = -1;
+    public int cellX = -1;
 
     /**
      * Indicates the Y position of the associated cell.
      */
-    int cellY = -1;
+    public int cellY = -1;
 
     /**
      * Indicates the X cell span.
      */
-    int spanX = 1;
+    public int spanX = 1;
 
     /**
      * Indicates the Y cell span.
@@ -80,17 +80,17 @@
     /**
      * Indicates the minimum X cell span.
      */
-    int minSpanX = 1;
+    public int minSpanX = 1;
 
     /**
      * Indicates the minimum Y cell span.
      */
-    int minSpanY = 1;
+    public int minSpanY = 1;
 
     /**
      * Indicates that this item needs to be updated in the db
      */
-    boolean requiresDbUpdate = false;
+    public boolean requiresDbUpdate = false;
 
     /**
      * Title of the item
@@ -118,7 +118,7 @@
         LauncherModel.checkItemInfo(this);
     }
 
-    protected Intent getIntent() {
+    public Intent getIntent() {
         throw new RuntimeException("Unexpected Intent");
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c22a6bf..ff5b1eb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,6 +96,7 @@
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.PagedView.PageSwitchListener;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -119,7 +120,7 @@
  */
 public class Launcher extends Activity
         implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
-                   View.OnTouchListener {
+                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
     static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -190,6 +191,7 @@
     private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
 
 
+    static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
     static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
 
     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
@@ -212,6 +214,7 @@
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE = 400;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
+    private static final int ACTIVITY_START_DELAY = 1000;
 
     private static final Object sLock = new Object();
     private static int sScreen = DEFAULT_SCREEN;
@@ -393,7 +396,7 @@
 
         LauncherAppState.setApplicationContext(getApplicationContext());
         LauncherAppState app = LauncherAppState.getInstance();
-
+        LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
         // Determine the dynamic grid properties
         Point smallestSize = new Point();
         Point largestSize = new Point();
@@ -480,10 +483,15 @@
         // On large interfaces, we want the screen to auto-rotate based on the current orientation
         unlockScreenOrientation(true);
 
+        if (shouldShowIntroScreen()) {
+            showIntroScreen();
+        } else {
+            showFirstRunActivity();
+        }
+
         // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
         // on the device, then we always show the first run cling experience (or if there is no
         // launcher2). Otherwise, we prompt the user upon started for migration
-        showFirstRunActivity();
         if (mLauncherClings.shouldShowFirstRunOrMigrationClings()) {
             if (mModel.canMigrateFromOldLauncherDb(this)) {
                 mLauncherClings.showMigrationCling();
@@ -495,6 +503,9 @@
         }
     }
 
+    @Override
+    public void onLauncherProviderChange() { }
+
     protected void onUserLeaveHint() {
         super.onUserLeaveHint();
         sPausedFromUserAction = true;
@@ -514,21 +525,6 @@
     }
 
     /**
-     * To be overridden by subclasses to indicate that there is an activity to launch
-     * before showing the standard launcher experience.
-     */
-    protected boolean hasFirstRunActivity() {
-        return false;
-    }
-
-    /**
-     * To be overridden by subclasses to launch any first run activity
-     */
-    protected Intent getFirstRunActivity() {
-        return null;
-    }
-
-    /**
      * Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
      * ensure the custom content page is added or removed if necessary.
      */
@@ -1010,7 +1006,7 @@
             // It is also poassible that onShow will instead be called slightly after first layout
             // if PagedView#setRestorePage was set to the custom content page in onCreate().
             if (mWorkspace.isOnOrMovingToCustomContent()) {
-                mWorkspace.getCustomContentCallbacks().onShow();
+                mWorkspace.getCustomContentCallbacks().onShow(true);
             }
         }
         mWorkspace.updateInteractionForState();
@@ -1054,8 +1050,9 @@
     }
 
     public interface CustomContentCallbacks {
-        // Custom content is completely shown
-        public void onShow();
+        // Custom content is completely shown. {@code fromResume} indicates whether this was caused
+        // by a onResume or by scrolling otherwise.
+        public void onShow(boolean fromResume);
 
         // Custom content is completely hidden
         public void onHide();
@@ -1068,9 +1065,6 @@
         return false;
     }
 
-    protected void startSettings() {
-    }
-
     public interface QSBScroller {
         public void setScrollY(int scrollY);
     }
@@ -1233,6 +1227,7 @@
         mLauncherView = findViewById(R.id.launcher);
         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(
@@ -1255,7 +1250,7 @@
             @Override
             public void onClick(View arg0) {
                 if (!mWorkspace.isSwitchingState()) {
-                    showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+                    onClickAddWidgetButton(arg0);
                 }
             }
         });
@@ -1266,7 +1261,7 @@
             @Override
             public void onClick(View arg0) {
                 if (!mWorkspace.isSwitchingState()) {
-                    startWallpaper();
+                    onClickWallpaperPicker(arg0);
                 }
             }
         });
@@ -1278,9 +1273,9 @@
                 @Override
                 public void onClick(View arg0) {
                     if (!mWorkspace.isSwitchingState()) {
-                        startSettings();
+                        onClickSettingsButton(arg0);
                     }
-                    }
+                }
             });
             settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
         } else {
@@ -1334,6 +1329,17 @@
     }
 
     /**
+     * Sets the all apps button. This method is called from {@link Hotseat}.
+     */
+    public void setAllAppsButton(View allAppsButton) {
+        mAllAppsButton = allAppsButton;
+    }
+
+    public View getAllAppsButton() {
+        return mAllAppsButton;
+    }
+
+    /**
      * Creates a view representing a shortcut.
      *
      * @param info The data structure describing the shortcut.
@@ -2260,12 +2266,6 @@
         sFolders.remove(folder.id);
     }
 
-    protected void startWallpaper() {
-        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
-        pickWallpaper.setComponent(getWallpaperPickerComponent());
-        startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
-    }
-
     protected ComponentName getWallpaperPickerComponent() {
         return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName());
     }
@@ -2369,51 +2369,13 @@
 
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
-            // Open shortcut
-            final ShortcutInfo shortcut = (ShortcutInfo) tag;
-            final Intent intent = shortcut.intent;
-
-            // Check for special shortcuts
-            if (intent.getComponent() != null) {
-                final String shortcutClass = intent.getComponent().getClassName();
-
-                if (shortcutClass.equals(WidgetAdder.class.getName())) {
-                    onClickAddWidgetButton();
-                    return;
-                } else if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
-                    MemoryDumpActivity.startDump(this);
-                    return;
-                } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
-                    toggleShowWeightWatcher();
-                    return;
-                }
-            }
-
-            // Start activities
-            int[] pos = new int[2];
-            v.getLocationOnScreen(pos);
-            intent.setSourceBounds(new Rect(pos[0], pos[1],
-                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-
-            boolean success = startActivitySafely(v, intent, tag);
-
-            mStats.recordLaunch(intent, shortcut);
-
-            if (success && v instanceof BubbleTextView) {
-                mWaitingForResume = (BubbleTextView) v;
-                mWaitingForResume.setStayPressed(true);
-            }
+            onClickAppShortcut(v);
         } else if (tag instanceof FolderInfo) {
             if (v instanceof FolderIcon) {
-                FolderIcon fi = (FolderIcon) v;
-                handleFolderClick(fi);
+                onClickFolderIcon(v);
             }
         } else if (v == mAllAppsButton) {
-            if (isAllAppsVisible()) {
-                showWorkspace(true);
-            } else {
-                onClickAllAppsButton(v);
-            }
+            onClickAllAppsButton(v);
         }
     }
 
@@ -2467,18 +2429,144 @@
      *
      * @param v The view that was clicked.
      */
-    public void onClickAllAppsButton(View v) {
-        showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+    protected void onClickAllAppsButton(View v) {
+        if (LOGD) Log.d(TAG, "onClickAllAppsButton");
+        if (isAllAppsVisible()) {
+            showWorkspace(true);
+        } else {
+            showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+        }
+    }
+
+    /**
+     * Event handler for a paged view icon click.
+     * @param v The view that was clicked.
+     * @param appInfo The {link AppInfo} of the view.
+     */
+    public void onClickPagedViewIcon(View v, AppInfo appInfo) {
+        if (LOGD) Log.d(TAG, "onClickPagedViewIcon");
+        startActivitySafely(v, appInfo.intent, appInfo);
+        getStats().recordLaunch(appInfo.intent);
+    }
+
+    /**
+     * Event handler for an app shortcut click.
+     *
+     * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+     */
+    protected void onClickAppShortcut(View v) {
+        if (LOGD) Log.d(TAG, "onClickAppShortcut");
+        Object tag = v.getTag();
+        if (!(tag instanceof ShortcutInfo)) {
+            throw new IllegalArgumentException("Input must be a Shortcut");
+        }
+
+        // Open shortcut
+        final ShortcutInfo shortcut = (ShortcutInfo) tag;
+        final Intent intent = shortcut.intent;
+
+        // Check for special shortcuts
+        if (intent.getComponent() != null) {
+            final String shortcutClass = intent.getComponent().getClassName();
+
+            if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
+                MemoryDumpActivity.startDump(this);
+                return;
+            } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
+                toggleShowWeightWatcher();
+                return;
+            }
+        }
+
+        // Start activities
+        int[] pos = new int[2];
+        v.getLocationOnScreen(pos);
+        intent.setSourceBounds(new Rect(pos[0], pos[1],
+                pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+
+        boolean success = startActivitySafely(v, intent, tag);
+
+        mStats.recordLaunch(intent, shortcut);
+
+        if (success && v instanceof BubbleTextView) {
+            mWaitingForResume = (BubbleTextView) v;
+            mWaitingForResume.setStayPressed(true);
+        }
+    }
+
+    /**
+     * Event handler for a folder icon click.
+     *
+     * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+     */
+    protected void onClickFolderIcon(View v) {
+        if (LOGD) Log.d(TAG, "onClickFolder");
+        if (!(v instanceof FolderIcon)){
+            throw new IllegalArgumentException("Input must be a FolderIcon");
+        }
+
+        FolderIcon folderIcon = (FolderIcon) v;
+        final FolderInfo info = folderIcon.getFolderInfo();
+        Folder openFolder = mWorkspace.getFolderForTag(info);
+
+        // If the folder info reports that the associated folder is open, then verify that
+        // it is actually opened. There have been a few instances where this gets out of sync.
+        if (info.opened && openFolder == null) {
+            Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
+                    + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
+            info.opened = false;
+        }
+
+        if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
+            // Close any open folder
+            closeFolder();
+            // Open the requested folder
+            openFolder(folderIcon);
+        } else {
+            // Find the open folder...
+            int folderScreen;
+            if (openFolder != null) {
+                folderScreen = mWorkspace.getPageForView(openFolder);
+                // .. and close it
+                closeFolder(openFolder);
+                if (folderScreen != mWorkspace.getCurrentPage()) {
+                    // Close any folder open on the current screen
+                    closeFolder();
+                    // Pull the folder onto this screen
+                    openFolder(folderIcon);
+                }
+            }
+        }
     }
 
     /**
      * Event handler for the (Add) Widgets button that appears after a long press
      * on the home screen.
      */
-    protected void onClickAddWidgetButton() {
+    protected void onClickAddWidgetButton(View view) {
+        if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
         showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
     }
 
+    /**
+     * Event handler for the wallpaper picker button that appears after a long press
+     * on the home screen.
+     */
+    protected void onClickWallpaperPicker(View v) {
+        if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
+        final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
+        pickWallpaper.setComponent(getWallpaperPickerComponent());
+        startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
+    }
+
+    /**
+     * Event handler for a click on the settings button that appears after a long press
+     * on the home screen.
+     */
+    protected void onClickSettingsButton(View v) {
+        if (LOGD) Log.d(TAG, "onClickSettingsButton");
+    }
+
     public void onTouchDownAllAppsButton(View v) {
         // Provide the same haptic feedback that the system offers for virtual keys.
         v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
@@ -2514,6 +2602,8 @@
         }
     }
 
+    public void onDragStarted(View view) {}
+
     /**
      * Called when the user stops interacting with the launcher.
      * This implies that the user is now on the homescreen and is not doing housekeeping.
@@ -2598,40 +2688,6 @@
         return success;
     }
 
-    private void handleFolderClick(FolderIcon folderIcon) {
-        final FolderInfo info = folderIcon.getFolderInfo();
-        Folder openFolder = mWorkspace.getFolderForTag(info);
-
-        // If the folder info reports that the associated folder is open, then verify that
-        // it is actually opened. There have been a few instances where this gets out of sync.
-        if (info.opened && openFolder == null) {
-            Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
-                    + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
-            info.opened = false;
-        }
-
-        if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
-            // Close any open folder
-            closeFolder();
-            // Open the requested folder
-            openFolder(folderIcon);
-        } else {
-            // Find the open folder...
-            int folderScreen;
-            if (openFolder != null) {
-                folderScreen = mWorkspace.getPageForView(openFolder);
-                // .. and close it
-                closeFolder(openFolder);
-                if (folderScreen != mWorkspace.getCurrentPage()) {
-                    // Close any folder open on the current screen
-                    closeFolder();
-                    // Pull the folder onto this screen
-                    openFolder(folderIcon);
-                }
-            }
-        }
-    }
-
     /**
      * This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
      * in the DragLayer in the exact absolute location of the original FolderIcon.
@@ -2891,7 +2947,7 @@
                 mWorkspaceBackgroundDrawable : null);
     }
 
-    void updateWallpaperVisibility(boolean visible) {
+    protected void changeWallpaperVisiblity(boolean visible) {
         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
         int curflags = getWindow().getAttributes().flags
                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -3926,7 +3982,15 @@
                     if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
                         CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
                         if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
-                            throw new RuntimeException("OCCUPIED");
+                            View v = cl.getChildAt(item.cellX, item.cellY);
+                            Object tag = v.getTag();
+                            String desc = "Collision while binding workspace item: " + item
+                                    + ". Collides with " + tag;
+                            if (LauncherAppState.isDogfoodBuild()) {
+                                throw (new RuntimeException(desc));
+                            } else {
+                                Log.d(TAG, desc);
+                            }
                         }
                     }
 
@@ -4186,6 +4250,17 @@
     }
 
     /**
+     * Update the state of a package, typically related to install state.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void updatePackageState(String pkgName, int state) {
+        if (mWorkspace != null) {
+            mWorkspace.updatePackageState(pkgName, state);
+        }
+    }
+
+    /**
      * A package was uninstalled.  We take both the super set of packageNames
      * in addition to specific applications to remove, the reason being that
      * this can be called when a package is updated as well.  In that scenario,
@@ -4366,20 +4441,42 @@
         mLauncherClings.dismissFolderCling(v);
     }
 
+
+    /**
+     * To be overridden by subclasses to indicate that there is an activity to launch
+     * before showing the standard launcher experience.
+     */
+    protected boolean hasFirstRunActivity() {
+        return false;
+    }
+
+    /**
+     * To be overridden by subclasses to launch any first run activity
+     */
+    protected Intent getFirstRunActivity() {
+        return null;
+    }
+
     private boolean shouldRunFirstRunActivity() {
         return !ActivityManager.isRunningInTestHarness() &&
                 !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
     }
 
-    public void showFirstRunActivity() {
+    protected boolean hasRunFirstRunActivity() {
+        return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
+    }
+
+    public boolean showFirstRunActivity() {
         if (shouldRunFirstRunActivity() &&
                 hasFirstRunActivity()) {
             Intent firstRunIntent = getFirstRunActivity();
             if (firstRunIntent != null) {
                 startActivity(firstRunIntent);
                 markFirstRunActivityShown();
+                return true;
             }
         }
+        return false;
     }
 
     private void markFirstRunActivityShown() {
@@ -4388,6 +4485,61 @@
         editor.apply();
     }
 
+    /**
+     * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
+     * screen that must be displayed and dismissed.
+     */
+    protected boolean hasDismissableIntroScreen() {
+        return false;
+    }
+
+    /**
+     * Full screen intro screen to be shown and dismissed before the launcher can be used.
+     */
+    protected View getIntroScreen() {
+        return null;
+    }
+
+    /**
+     * To be overriden by subclasses to indicate whether the in-activity intro screen has been
+     * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
+     */
+    private boolean shouldShowIntroScreen() {
+        return hasDismissableIntroScreen() &&
+                !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
+    }
+
+    protected void showIntroScreen() {
+        View introScreen = getIntroScreen();
+        changeWallpaperVisiblity(false);
+        if (introScreen != null) {
+            mDragLayer.showOverlayView(introScreen);
+        }
+    }
+
+    public void dismissIntroScreen() {
+        markIntroScreenDismissed();
+        if (showFirstRunActivity()) {
+            // We delay hiding the intro view until the first run activity is showing. This
+            // avoids a blip.
+            mWorkspace.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mDragLayer.dismissOverlayView();
+                }
+            }, ACTIVITY_START_DELAY);
+        } else {
+            mDragLayer.dismissOverlayView();
+        }
+        changeWallpaperVisiblity(true);
+    }
+
+    private void markIntroScreenDismissed() {
+        SharedPreferences.Editor editor = mSharedPrefs.edit();
+        editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
+        editor.apply();
+    }
+
     void showWorkspaceSearchAndHotseat() {
         if (mWorkspace != null) mWorkspace.setAlpha(1f);
         if (mHotseat != null) mHotseat.setAlpha(1f);
@@ -4402,7 +4554,6 @@
         if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
     }
 
-
     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
         ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
         if (ri == null) {
@@ -4416,10 +4567,18 @@
         return new ShortcutInfo(shortcutIntent, caption, icon);
     }
 
+    protected void moveWorkspaceToDefaultScreen() {
+        mWorkspace.moveToDefaultScreen(false);
+    }
+
     public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
         dragView.setTag(dragInfo);
-        mWorkspace.onDragStartedWithItem(dragView);
-        mWorkspace.beginDragShared(dragView, source);
+        mWorkspace.onExternalDragStartedWithItem(dragView);
+        mWorkspace.beginExternalDragShared(dragView, source);
+    }
+
+    @Override
+    public void onPageSwitch(View newPage, int newPageIndex) {
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 29e18f9..5ddafea 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -30,6 +30,8 @@
     private static final String TAG = "LauncherAppState";
     private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
 
+    private static final boolean DEBUG = false;
+
     private final AppFilter mAppFilter;
     private final BuildInfo mBuildInfo;
     private LauncherModel mModel;
@@ -249,4 +251,9 @@
     public static boolean isDogfoodBuild() {
         return getInstance().mBuildInfo.isDogfoodBuild();
     }
+
+    public void setPackageState(String pkgName, int state) {
+        if (DEBUG) Log.d(TAG, "setPackageState(" + pkgName + ", " +  state  + ")");
+        mModel.setPackageState(pkgName, state);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 28df90f..950828e 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -23,7 +23,7 @@
 /**
  * Represents a widget (either instantiated or about to be) in the Launcher.
  */
-class LauncherAppWidgetInfo extends ItemInfo {
+public class LauncherAppWidgetInfo extends ItemInfo {
 
     /**
      * Indicates that the widget hasn't been instantiated yet.
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 62e6f31..cab55c7 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -461,7 +461,8 @@
 
         int startRows = out.rows;
         if (DEBUG) Log.d(TAG, "starting here: " + startRows);
-        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION;
+        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
+                Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT;
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
         Set<String> currentIds = new HashSet<String>(cursor.getCount());
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 952edfd..97138ee 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -21,8 +21,11 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserManager;
@@ -262,12 +265,21 @@
             Cling c = initCling(R.id.workspace_cling, 0, false, true);
             c.updateWorkspaceBubblePosition();
 
-            // Set the focused hotseat app if there is one
-            c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
-                    mLauncher.getFirstRunFocusedHotseatAppRank(),
-                    mLauncher.getFirstRunFocusedHotseatAppComponentName(),
-                    mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
-                    mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
+            try {
+                // We only enable the focused hotseat app if we are preinstalled
+                PackageManager pm = mLauncher.getPackageManager();
+                ApplicationInfo ai = pm.getApplicationInfo(mLauncher.getPackageName(), 0);
+                if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    // Set the focused hotseat app
+                    c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
+                        mLauncher.getFirstRunFocusedHotseatAppRank(),
+                        mLauncher.getFirstRunFocusedHotseatAppComponentName(),
+                        mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
+                        mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                e.printStackTrace();
+            }
         } else {
             removeCling(R.id.workspace_cling);
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 007fd7a..d8645aa 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -173,6 +173,7 @@
                                   ArrayList<ItemInfo> addAnimated,
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
+        public void updatePackageState(String pkgName, int state);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
@@ -191,8 +192,12 @@
         ContentResolver contentResolver = context.getContentResolver();
 
         mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
-        mOldContentProviderExists = (contentResolver.acquireContentProviderClient(
-                LauncherSettings.Favorites.OLD_CONTENT_URI) != null);
+        ContentProviderClient client = contentResolver.acquireContentProviderClient(
+                Uri.parse(context.getString(R.string.old_launcher_provider_uri)));
+        mOldContentProviderExists = (client != null);
+        if (client != null) {
+            client.release();
+        }
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mIconCache = iconCache;
@@ -292,6 +297,19 @@
         return null;
     }
 
+    public void setPackageState(final String pkgName, final int state) {
+        // Process the updated package state
+        Runnable r = new Runnable() {
+            public void run() {
+                Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+                if (callbacks != null) {
+                    callbacks.updatePackageState(pkgName, state);
+                }
+            }
+        };
+        mHandler.post(r);
+    }
+
     public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
         final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
 
@@ -953,6 +971,7 @@
         values.put(LauncherSettings.Favorites._ID, item.id);
         item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
 
+        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
         Runnable r = new Runnable() {
             public void run() {
                 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
@@ -960,7 +979,7 @@
 
                 // Lock on mBgLock *after* the db operation
                 synchronized (sBgLock) {
-                    checkItemInfoLocked(item.id, item, null);
+                    checkItemInfoLocked(item.id, item, stackTrace);
                     sBgItemsIdMap.put(item.id, item);
                     switch (item.itemType) {
                         case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -2189,7 +2208,12 @@
                                 line += " | ";
                             }
                             for (int x = 0; x < countX; x++) {
-                                line += ((occupied.get(screenId)[x][y] != null) ? "#" : ".");
+                                ItemInfo[][] screen = occupied.get(screenId);
+                                if (x < screen.length && y < screen[x].length) {
+                                    line += (screen[x][y] != null) ? "#" : ".";
+                                } else {
+                                    line += "!";
+                                }
                             }
                         }
                         Log.d(TAG, "[ " + line + " ]");
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a080dd8..0e559a8 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -32,7 +32,9 @@
 import android.content.OperationApplicationException;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -72,7 +74,7 @@
 
     private static final String DATABASE_NAME = "launcher.db";
 
-    private static final int DATABASE_VERSION = 17;
+    private static final int DATABASE_VERSION = 18;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -93,6 +95,8 @@
     private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
             "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
 
+    private LauncherProviderChangeListener mListener;
+
     /**
      * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
      * {@link AppWidgetHost#deleteHost()} is called during database creation.
@@ -116,6 +120,10 @@
         return mOpenHelper.wasNewDbCreated();
     }
 
+    public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
+        mListener = listener;
+    }
+
     @Override
     public String getType(Uri uri) {
         SqlArguments args = new SqlArguments(uri, null, null);
@@ -242,6 +250,9 @@
 
         // always notify the backup agent
         LauncherBackupAgentHelper.dataChanged(getContext());
+        if (mListener != null) {
+            mListener.onLauncherProviderChange();
+        }
     }
 
     private void addModifiedTime(ContentValues values) {
@@ -318,7 +329,7 @@
 
     public void migrateLauncher2Shortcuts() {
         mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
-                LauncherSettings.Favorites.OLD_CONTENT_URI);
+                Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
     }
 
     private static int getDefaultWorkspaceResourceId() {
@@ -352,6 +363,7 @@
     }
 
     private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String TAG_RESOLVE = "resolve";
         private static final String TAG_FAVORITES = "favorites";
         private static final String TAG_FAVORITE = "favorite";
         private static final String TAG_CLOCK = "clock";
@@ -454,7 +466,7 @@
                         "/old_favorites?notify=true");
                 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
                     // Try and upgrade from the Launcher2 db
-                    uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
+                    uri = Uri.parse(mContext.getString(R.string.old_launcher_provider_uri));
                     if (!convertDatabase(db, uri, permuteScreensCb, false)) {
                         // If we fail, then set a flag to load the default workspace
                         setFlagEmptyDbCreated();
@@ -480,6 +492,13 @@
                     ");");
         }
 
+        private void removeOrphanedItems(SQLiteDatabase db) {
+            db.execSQL("DELETE FROM " + TABLE_FAVORITES + " WHERE " +
+                    LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
+                    LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS +
+                    ")");
+        }
+
         private void setFlagJustLoadedOldDb() {
             String spKey = LauncherAppState.getSharedPreferencesKey();
             SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
@@ -780,6 +799,17 @@
                 version = 17;
             }
 
+            if (version < 18) {
+                // Due to a data loss bug, some users may have items associated with screen ids
+                // which no longer exist. Since this can cause other problems, and since the user
+                // will never see these items anyway, we use database upgrade as an opportunity to
+                // clean things up.
+
+                // TODO: this needs to be fixed, currently causes data loss.
+                //removeOrphanedItems(db);
+                version = 18;
+            }
+
             if (version != DATABASE_VERSION) {
                 Log.w(TAG, "Destroying all old data.");
                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
@@ -1216,6 +1246,32 @@
                     } else if (TAG_SHORTCUT.equals(name)) {
                         long id = addUriShortcut(db, values, a);
                         added = id >= 0;
+                    } else if (TAG_RESOLVE.equals(name)) {
+                        // This looks through the contained favorites (or meta-favorites) and
+                        // attempts to add them as shortcuts in the fallback group's location
+                        // until one is added successfully.
+                        added = false;
+                        final int groupDepth = parser.getDepth();
+                        while ((type = parser.next()) != XmlPullParser.END_TAG ||
+                                parser.getDepth() > groupDepth) {
+                            if (type != XmlPullParser.START_TAG) {
+                                continue;
+                            }
+                            final String fallback_item_name = parser.getName();
+                            final TypedArray ar = mContext.obtainStyledAttributes(attrs,
+                                    R.styleable.Favorite);
+                            if (!added) {
+                                if (TAG_FAVORITE.equals(fallback_item_name)) {
+                                    final long id =
+                                        addAppShortcut(db, values, ar, packageManager, intent);
+                                    added = id >= 0;
+                                } else {
+                                    Log.e(TAG, "Fallback groups can contain only favorites "
+                                            + ar.toString());
+                                }
+                            }
+                            ar.recycle();
+                        }
                     } else if (TAG_FOLDER.equals(name)) {
                         String title;
                         int titleResId =  a.getResourceId(R.styleable.Favorite_title, -1);
@@ -1298,41 +1354,137 @@
             return i;
         }
 
-        private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
+        // A meta shortcut attempts to resolve an intent specified as a URI in the XML, if a
+        // logical choice for what shortcut should be used for that intent exists, then it is
+        // added. Otherwise add nothing.
+        private long addAppShortcutByUri(SQLiteDatabase db, ContentValues values, TypedArray a,
                 PackageManager packageManager, Intent intent) {
-            long id = -1;
-            ActivityInfo info;
-            String packageName = a.getString(R.styleable.Favorite_packageName);
-            String className = a.getString(R.styleable.Favorite_className);
+            final String intentUri = a.getString(R.styleable.Favorite_uri);
+
+            Intent metaIntent;
             try {
-                ComponentName cn;
-                try {
-                    cn = new ComponentName(packageName, className);
-                    info = packageManager.getActivityInfo(cn, 0);
-                } catch (PackageManager.NameNotFoundException nnfe) {
-                    String[] packages = packageManager.currentToCanonicalPackageNames(
-                        new String[] { packageName });
-                    cn = new ComponentName(packages[0], className);
-                    info = packageManager.getActivityInfo(cn, 0);
-                }
-                id = generateNewItemId();
-                intent.setComponent(cn);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                values.put(Favorites.INTENT, intent.toUri(0));
-                values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
-                values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
-                values.put(Favorites.SPANX, 1);
-                values.put(Favorites.SPANY, 1);
-                values.put(Favorites._ID, generateNewItemId());
-                if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
+                metaIntent = Intent.parseUri(intentUri, 0);
+            } catch (URISyntaxException e) {
+                Log.e(TAG, "Unable to add meta-favorite: " + intentUri, e);
+                return -1;
+            }
+
+            ResolveInfo resolved = packageManager.resolveActivity(metaIntent,
+                    PackageManager.MATCH_DEFAULT_ONLY);
+            final List<ResolveInfo> appList = packageManager.queryIntentActivities(
+                    metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
+
+            // Verify that the result is an app and not just the resolver dialog asking which
+            // app to use.
+            if (wouldLaunchResolverActivity(resolved, appList)) {
+                // If only one of the results is a system app then choose that as the default.
+                final ResolveInfo systemApp = getSingleSystemActivity(appList, packageManager);
+                if (systemApp == null) {
+                    // There is no logical choice for this meta-favorite, so rather than making
+                    // a bad choice just add nothing.
+                    Log.w(TAG, "No preference or single system activity found for "
+                            + metaIntent.toString());
                     return -1;
                 }
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.w(TAG, "Unable to add favorite: " + packageName +
-                        "/" + className, e);
+                resolved = systemApp;
             }
-            return id;
+            final ActivityInfo info = resolved.activityInfo;
+            intent.setComponent(new ComponentName(info.packageName, info.name));
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                    Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+            return addAppShortcut(db, values, info.loadLabel(packageManager).toString(), intent);
+        }
+
+        private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList,
+                PackageManager packageManager) {
+            ResolveInfo systemResolve = null;
+            final int N = appList.size();
+            for (int i = 0; i < N; ++i) {
+                try {
+                    ApplicationInfo info = packageManager.getApplicationInfo(
+                            appList.get(i).activityInfo.packageName, 0);
+                    if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        if (systemResolve != null) {
+                            return null;
+                        } else {
+                            systemResolve = appList.get(i);
+                        }
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Unable to get info about resolve results", e);
+                    return null;
+                }
+            }
+            return systemResolve;
+        }
+
+        private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
+                List<ResolveInfo> appList) {
+            // If the list contains the above resolved activity, then it can't be
+            // ResolverActivity itself.
+            for (int i = 0; i < appList.size(); ++i) {
+                ResolveInfo tmp = appList.get(i);
+                if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+                        && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
+                PackageManager packageManager, Intent intent) {
+            if (a.hasValue(R.styleable.Favorite_packageName)
+                    && a.hasValue(R.styleable.Favorite_className)) {
+                ActivityInfo info;
+                String packageName = a.getString(R.styleable.Favorite_packageName);
+                String className = a.getString(R.styleable.Favorite_className);
+                try {
+                    ComponentName cn;
+                    try {
+                        cn = new ComponentName(packageName, className);
+                        info = packageManager.getActivityInfo(cn, 0);
+                    } catch (PackageManager.NameNotFoundException nnfe) {
+                        String[] packages = packageManager.currentToCanonicalPackageNames(
+                                new String[] { packageName });
+                        cn = new ComponentName(packages[0], className);
+                        info = packageManager.getActivityInfo(cn, 0);
+                    }
+                    intent.setComponent(cn);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+                    return addAppShortcut(db, values, info.loadLabel(packageManager).toString(),
+                            intent);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Unable to add favorite: " + packageName +
+                            "/" + className, e);
+                }
+                return -1;
+            } else if (a.hasValue(R.styleable.Favorite_uri)) {
+                // If no component specified try to find a shortcut to add from the URI.
+                return addAppShortcutByUri(db, values, a, packageManager, intent);
+            } else {
+                Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
+                return -1;
+            }
+        }
+
+        private long addAppShortcut(SQLiteDatabase db, ContentValues values, String title,
+                Intent intent) {
+            long id = generateNewItemId();
+            values.put(Favorites.INTENT, intent.toUri(0));
+            values.put(Favorites.TITLE, title);
+            values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
+            values.put(Favorites.SPANX, 1);
+            values.put(Favorites.SPANY, 1);
+            values.put(Favorites._ID, id);
+            if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
+                return -1;
+            } else {
+                return id;
+            }
         }
 
         private long addFolder(SQLiteDatabase db, ContentValues values) {
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
new file mode 100644
index 0000000..0de96fb
--- /dev/null
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -0,0 +1,11 @@
+package com.android.launcher3;
+
+/**
+ * This class is a listener for {@link LauncherProvider} changes. It gets notified in the
+ * sendNotify method. This listener is needed because by default the Launcher suppresses
+ * standard data change callbacks.
+ */
+public interface LauncherProviderChangeListener {
+
+    public void onLauncherProviderChange();
+}
diff --git a/src/com/android/launcher3/PagedViewIconCache.java b/src/com/android/launcher3/PagedViewIconCache.java
deleted file mode 100644
index 93887ea..0000000
--- a/src/com/android/launcher3/PagedViewIconCache.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.pm.ComponentInfo;
-import android.content.pm.ResolveInfo;
-import android.graphics.Bitmap;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * Simple cache mechanism for PagedView outlines.
- */
-public class PagedViewIconCache {
-    public static class Key {
-        public enum Type {
-            ApplicationInfoKey,
-            AppWidgetProviderInfoKey,
-            ResolveInfoKey
-        }
-        private final ComponentName mComponentName;
-        private final Type mType;
-
-        public Key(AppInfo info) {
-            mComponentName = info.componentName;
-            mType = Type.ApplicationInfoKey;
-        }
-        public Key(ResolveInfo info) {
-            final ComponentInfo ci = info.activityInfo != null ? info.activityInfo :
-                info.serviceInfo;
-            mComponentName = new ComponentName(ci.packageName, ci.name);
-            mType = Type.ResolveInfoKey;
-        }
-        public Key(AppWidgetProviderInfo info) {
-            mComponentName = info.provider;
-            mType = Type.AppWidgetProviderInfoKey;
-        }
-
-        private ComponentName getComponentName() {
-            return mComponentName;
-        }
-        public boolean isKeyType(Type t) {
-            return (mType == t);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o instanceof Key) {
-                Key k = (Key) o;
-                return mComponentName.equals(k.mComponentName);
-            }
-            return super.equals(o);
-        }
-        @Override
-        public int hashCode() {
-            return getComponentName().hashCode();
-        }
-    }
-
-    private final HashMap<Key, Bitmap> mIconOutlineCache = new HashMap<Key, Bitmap>();
-
-    public void clear() {
-        for (Key key : mIconOutlineCache.keySet()) {
-            mIconOutlineCache.get(key).recycle();
-        }
-        mIconOutlineCache.clear();
-    }
-    private void retainAll(HashSet<Key> keysToKeep, Key.Type t) {
-        HashSet<Key> keysToRemove = new HashSet<Key>(mIconOutlineCache.keySet());
-        keysToRemove.removeAll(keysToKeep);
-        for (Key key : keysToRemove) {
-            if (key.isKeyType(t)) {
-                mIconOutlineCache.get(key).recycle();
-                mIconOutlineCache.remove(key);
-            }
-        }
-    }
-    /** Removes all the keys to applications that aren't in the passed in collection */
-    public void retainAllApps(ArrayList<AppInfo> keys) {
-        HashSet<Key> keysSet = new HashSet<Key>();
-        for (AppInfo info : keys) {
-            keysSet.add(new Key(info));
-        }
-        retainAll(keysSet, Key.Type.ApplicationInfoKey);
-    }
-    /** Removes all the keys to shortcuts that aren't in the passed in collection */
-    public void retainAllShortcuts(List<ResolveInfo> keys) {
-        HashSet<Key> keysSet = new HashSet<Key>();
-        for (ResolveInfo info : keys) {
-            keysSet.add(new Key(info));
-        }
-        retainAll(keysSet, Key.Type.ResolveInfoKey);
-    }
-    /** Removes all the keys to widgets that aren't in the passed in collection */
-    public void retainAllAppWidgets(List<AppWidgetProviderInfo> keys) {
-        HashSet<Key> keysSet = new HashSet<Key>();
-        for (AppWidgetProviderInfo info : keys) {
-            keysSet.add(new Key(info));
-        }
-        retainAll(keysSet, Key.Type.AppWidgetProviderInfoKey);
-    }
-    public void addOutline(Key key, Bitmap b) {
-        mIconOutlineCache.put(key, b);
-    }
-    public void removeOutline(Key key) {
-        if (mIconOutlineCache.containsKey(key)) {
-            mIconOutlineCache.get(key).recycle();
-            mIconOutlineCache.remove(key);
-        }
-    }
-    public Bitmap getOutline(Key key) {
-        return mIconOutlineCache.get(key);
-    }
-}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 79d114c..5afa784 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -31,7 +31,25 @@
 /**
  * Represents a launchable icon on the workspaces and in folders.
  */
-class ShortcutInfo extends ItemInfo {
+public class ShortcutInfo extends ItemInfo {
+
+    /** This package is not installed, and there is no other information available. */
+    public static final int PACKAGE_STATE_UNKNOWN = -2;
+
+    /** This package is not installed, because installation failed. */
+    public static final int PACKAGE_STATE_ERROR = -1;
+
+    /** This package is installed.  This is the typical case */
+    public static final int PACKAGE_STATE_DEFAULT = 0;
+
+    /** This package is not installed, but some external entity has promised to install it. */
+    public static final int PACKAGE_STATE_ENQUEUED = 1;
+
+    /** This package is not installed, but some external entity is downloading it. */
+    public static final int PACKAGE_STATE_DOWNLOADING = 2;
+
+    /** This package is not installed, but some external entity is installing it. */
+    public static final int PACKAGE_STATE_INSTALLING = 3;
 
     /**
      * The intent used to start the application.
@@ -74,7 +92,7 @@
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
 
-    protected Intent getIntent() {
+    public Intent getIntent() {
         return intent;
     }
 
@@ -219,5 +237,15 @@
                     + " customIcon=" + info.customIcon);
         }
     }
+
+    public boolean isPromise() {
+        return restoredIntent != null;
+    }
+
+    public boolean isPromiseFor(String pkgName) {
+        return restoredIntent != null
+                && pkgName != null
+                && pkgName.equals(restoredIntent.getComponent().getPackageName());
+    }
 }
 
diff --git a/src/com/android/launcher3/WidgetAdder.java b/src/com/android/launcher3/WidgetAdder.java
deleted file mode 100644
index 79ac504..0000000
--- a/src/com/android/launcher3/WidgetAdder.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.launcher3;
-
-import android.app.Activity;
-
-public class WidgetAdder extends Activity {
-
-}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 3db0b51..36152f8 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -15,6 +15,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
@@ -121,19 +122,20 @@
     private RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
     private RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
     private PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
+    private PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
     private String mCachedSelectQuery;
     private BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
 
     private int mAppIconSize;
     private IconCache mIconCache;
 
-    private final float sWidgetPreviewIconPaddingPercentage = 0.25f;
+    private static final float sWidgetPreviewIconPaddingPercentage = 0.25f;
 
     private CacheDb mDb;
 
-    private HashMap<String, WeakReference<Bitmap>> mLoadedPreviews;
-    private ArrayList<SoftReference<Bitmap>> mUnusedBitmaps;
-    private static HashSet<String> sInvalidPackages;
+    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews;
+    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps;
+    private final static HashSet<String> sInvalidPackages;
 
     static {
         sInvalidPackages = new HashSet<String>();
@@ -184,18 +186,19 @@
         final String name = getObjectName(o);
         final String packageName = getObjectPackage(o);
         // check if the package is valid
-        boolean packageValid = true;
         synchronized(sInvalidPackages) {
-            packageValid = !sInvalidPackages.contains(packageName);
+            boolean packageValid = !sInvalidPackages.contains(packageName);
+            if (!packageValid) {
+                return null;
+            }
         }
-        if (!packageValid) {
-            return null;
-        }
-        if (packageValid) {
-            synchronized(mLoadedPreviews) {
-                // check if it exists in our existing cache
-                if (mLoadedPreviews.containsKey(name) && mLoadedPreviews.get(name).get() != null) {
-                    return mLoadedPreviews.get(name).get();
+        synchronized(mLoadedPreviews) {
+            // check if it exists in our existing cache
+            if (mLoadedPreviews.containsKey(name)) {
+                WeakReference<Bitmap> bitmapReference = mLoadedPreviews.get(name);
+                Bitmap bitmap = bitmapReference.get();
+                if (bitmap != null) {
+                    return bitmap;
                 }
             }
         }
@@ -203,11 +206,13 @@
         Bitmap unusedBitmap = null;
         synchronized(mUnusedBitmaps) {
             // not in cache; we need to load it from the db
-            while ((unusedBitmap == null || !unusedBitmap.isMutable() ||
-                    unusedBitmap.getWidth() != mPreviewBitmapWidth ||
-                    unusedBitmap.getHeight() != mPreviewBitmapHeight)
-                    && mUnusedBitmaps.size() > 0) {
-                unusedBitmap = mUnusedBitmaps.remove(0).get();
+            while (unusedBitmap == null && mUnusedBitmaps.size() > 0) {
+                Bitmap candidate = mUnusedBitmaps.remove(0).get();
+                if (candidate != null && candidate.isMutable() &&
+                        candidate.getWidth() == mPreviewBitmapWidth &&
+                        candidate.getHeight() == mPreviewBitmapHeight) {
+                    unusedBitmap = candidate;
+                }
             }
             if (unusedBitmap != null) {
                 final Canvas c = mCachedAppWidgetPreviewCanvas.get();
@@ -221,12 +226,7 @@
             unusedBitmap = Bitmap.createBitmap(mPreviewBitmapWidth, mPreviewBitmapHeight,
                     Bitmap.Config.ARGB_8888);
         }
-
-        Bitmap preview = null;
-
-        if (packageValid) {
-            preview = readFromDb(name, unusedBitmap);
-        }
+        Bitmap preview = readFromDb(name, unusedBitmap);
 
         if (preview != null) {
             synchronized(mLoadedPreviews) {
@@ -520,14 +520,19 @@
             previewWidth = previewDrawableWidth * cellHSpan;
             previewHeight = previewDrawableHeight * cellVSpan;
 
-            defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight,
-                    Config.ARGB_8888);
+            defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
             final Canvas c = mCachedAppWidgetPreviewCanvas.get();
             c.setBitmap(defaultPreview);
-            previewDrawable.setBounds(0, 0, previewWidth, previewHeight);
-            previewDrawable.setTileModeXY(Shader.TileMode.REPEAT,
-                    Shader.TileMode.REPEAT);
-            previewDrawable.draw(c);
+            Paint p = mDefaultAppWidgetPreviewPaint.get();
+            if (p == null) {
+                p = new Paint();
+                p.setShader(new BitmapShader(previewDrawable.getBitmap(),
+                        Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+                mDefaultAppWidgetPreviewPaint.set(p);
+            }
+            final Rect dest = mCachedAppWidgetPreviewDestRect.get();
+            dest.set(0, 0, previewWidth, previewHeight);
+            c.drawRect(dest, p);
             c.setBitmap(null);
 
             // Draw the icon in the top left corner
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 567abfa..9800cf3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -37,6 +37,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -59,7 +60,6 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
-
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -96,6 +96,9 @@
 
     private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
 
+    private static final boolean MAP_NO_RECURSE = false;
+    private static final boolean MAP_RECURSE = true;
+
     // These animators are used to fade the children's outlines
     private ObjectAnimator mChildrenOutlineFadeInAnimation;
     private ObjectAnimator mChildrenOutlineFadeOutAnimation;
@@ -693,6 +696,12 @@
         // Log to disk
         Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true);
 
+        if (mLauncher.isWorkspaceLoading()) {
+            // Invalid and dangerous operation if workspace is loading
+            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
+            return;
+        }
+
         if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
         long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
 
@@ -723,6 +732,12 @@
             final int delay, final boolean stripEmptyScreens) {
         // Log to disk
         Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true);
+        if (mLauncher.isWorkspaceLoading()) {
+            // Don't strip empty screens if the workspace is still loading
+            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
+            return;
+        }
+
         if (delay > 0) {
             postDelayed(new Runnable() {
                 @Override
@@ -807,6 +822,11 @@
     public long commitExtraEmptyScreen() {
         // Log to disk
         Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true);
+        if (mLauncher.isWorkspaceLoading()) {
+            // Invalid and dangerous operation if workspace is loading
+            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
+            return -1;
+        }
 
         int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
         CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
@@ -864,7 +884,8 @@
         Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true);
 
         if (mLauncher.isWorkspaceLoading()) {
-            // Don't strip empty screens if the workspace is still loading
+            // Don't strip empty screens if the workspace is still loading.
+            // This is dangerous and can result in data loss.
             Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return;
         }
@@ -1215,7 +1236,7 @@
         if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
             mCustomContentShowing = true;
             if (mCustomContentCallbacks != null) {
-                mCustomContentCallbacks.onShow();
+                mCustomContentCallbacks.onShow(false);
                 mCustomContentShowTime = System.currentTimeMillis();
                 mLauncher.updateVoiceButtonProxyVisible(false);
             }
@@ -1971,6 +1992,45 @@
         mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
     }
 
+    private Rect getDrawableBounds(Drawable d) {
+        Rect bounds = new Rect();
+        d.copyBounds(bounds);
+        if (bounds.width() == 0 || bounds.height() == 0) {
+            bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+        }
+        return bounds;
+    }
+
+    public void onExternalDragStartedWithItem(View v) {
+        final Canvas canvas = new Canvas();
+
+        // Compose a drag bitmap with the view scaled to the icon size
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        int iconSize = grid.iconSizePx;
+        int bmpWidth = v.getMeasuredWidth();
+        int bmpHeight = v.getMeasuredHeight();
+
+        // If this is a text view, use its drawable instead
+        if (v instanceof TextView) {
+            TextView tv = (TextView) v;
+            Drawable d = tv.getCompoundDrawables()[1];
+            Rect bounds = getDrawableBounds(d);
+            bmpWidth = bounds.width();
+            bmpHeight = bounds.height();
+        }
+
+        // Compose the bitmap to create the icon from
+        Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(b);
+        drawDragView(v, c, 0, true);
+        c.setBitmap(null);
+
+        // The outline is used to visualize where the item will land if dropped
+        mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
+    }
+
     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
         final Canvas canvas = new Canvas();
 
@@ -2019,6 +2079,11 @@
     protected void onEndReordering() {
         super.onEndReordering();
 
+        if (mLauncher.isWorkspaceLoading()) {
+            // Invalid and dangerous operation if workspace is loading
+            return;
+        }
+
         hideOutlines();
         mScreenOrder.clear();
         int count = getChildCount();
@@ -2472,7 +2537,8 @@
         destCanvas.save();
         if (v instanceof TextView && pruneToDrawable) {
             Drawable d = ((TextView) v).getCompoundDrawables()[1];
-            clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
+            Rect bounds = getDrawableBounds(d);
+            clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
             destCanvas.translate(padding / 2, padding / 2);
             d.draw(destCanvas);
         } else {
@@ -2513,8 +2579,9 @@
 
         if (v instanceof TextView) {
             Drawable d = ((TextView) v).getCompoundDrawables()[1];
-            b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
-                    d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
+            Rect bounds = getDrawableBounds(d);
+            b = Bitmap.createBitmap(bounds.width() + padding,
+                    bounds.height() + padding, Bitmap.Config.ARGB_8888);
         } else {
             b = Bitmap.createBitmap(
                     v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
@@ -2595,6 +2662,7 @@
     }
 
     public void beginDragShared(View child, DragSource source) {
+        mLauncher.onDragStarted(child);
         // The drag bitmap follows the touch point around on the screen
         final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
 
@@ -2602,10 +2670,8 @@
         final int bmpHeight = b.getHeight();
 
         float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
-        int dragLayerX =
-                Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
-        int dragLayerY =
-                Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
+        int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
+        int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
                         - DRAG_BITMAP_PADDING / 2);
 
         LauncherAppState app = LauncherAppState.getInstance();
@@ -2655,6 +2721,52 @@
         b.recycle();
     }
 
+    public void beginExternalDragShared(View child, DragSource source) {
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        int iconSize = grid.iconSizePx;
+
+        // Notify launcher of drag start
+        mLauncher.onDragStarted(child);
+
+        // Compose a new drag bitmap that is of the icon size
+        final Bitmap tmpB = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
+        Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+        Paint p = new Paint();
+        p.setFilterBitmap(true);
+        Canvas c = new Canvas(b);
+        c.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
+                new Rect(0, 0, iconSize, iconSize), p);
+        c.setBitmap(null);
+
+        // Find the child's location on the screen
+        int bmpWidth = tmpB.getWidth();
+        float iconScale = (float) bmpWidth / iconSize;
+        float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
+        int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
+        int dragLayerY = Math.round(mTempXY[1]);
+
+        // Note: The drag region is used to calculate drag layer offsets, but the
+        // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
+        Point dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
+        Rect dragRect = new Rect(0, 0, iconSize, iconSize);
+
+        if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+            String msg = "Drag started with a view that has no tag set. This "
+                    + "will cause a crash (issue 11627249) down the line. "
+                    + "View: " + child + "  tag: " + child.getTag();
+            throw new IllegalStateException(msg);
+        }
+
+        // Start the drag
+        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
+                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
+        dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
+
+        // Recycle temporary bitmaps
+        tmpB.recycle();
+    }
+
     void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
             int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
         View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
@@ -4452,51 +4564,50 @@
         return childrenLayouts;
     }
 
-    public Folder getFolderForTag(Object tag) {
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
-                getAllShortcutAndWidgetContainers();
-        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            int count = layout.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View child = layout.getChildAt(i);
-                if (child instanceof Folder) {
-                    Folder f = (Folder) child;
+    public Folder getFolderForTag(final Object tag) {
+        final Folder[] value = new Folder[1];
+        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (v instanceof Folder) {
+                    Folder f = (Folder) v;
                     if (f.getInfo() == tag && f.getInfo().opened) {
-                        return f;
+                        value[0] = f;
+                        return true;
                     }
                 }
+                return false;
             }
-        }
-        return null;
+        });
+        return value[0];
     }
 
-    public View getViewForTag(Object tag) {
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
-                getAllShortcutAndWidgetContainers();
-        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            int count = layout.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View child = layout.getChildAt(i);
-                if (child.getTag() == tag) {
-                    return child;
+    public View getViewForTag(final Object tag) {
+        final View[] value = new View[1];
+        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (v.getTag() == tag) {
+                    value[0] = v;
+                    return true;
                 }
+                return false;
             }
-        }
-        return null;
+        });
+        return value[0];
     }
 
     void clearDropTargets() {
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
-                getAllShortcutAndWidgetContainers();
-        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            int childCount = layout.getChildCount();
-            for (int j = 0; j < childCount; j++) {
-                View v = layout.getChildAt(j);
+        mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
                 if (v instanceof DropTarget) {
                     mDragController.removeDropTarget((DropTarget) v);
                 }
+                // not done, process all the shortcuts
+                return false;
             }
-        }
+        });
     }
 
     // Removes ALL items that match a given package name, this is usually called when a package
@@ -4638,6 +4749,55 @@
         }
     }
 
+    interface ShortcutOperator {
+        /**
+         * Process the next shortcut, possibly with side-effect on {@link ShortcutOperator#value}.
+         *
+         * @param info info for the shortcut
+         * @param view view for the shortcut
+         * @param parent containing folder, or null
+         * @return true if done, false to continue the map
+         */
+        public boolean evaluate(ItemInfo info, View view, View parent);
+    }
+
+    /**
+     * Map the operator over the shortcuts, return the first-non-null value.
+     *
+     * @param recurse true: iterate over folder children. false: op get the folders themselves.
+     * @param op the operator to map over the shortcuts
+     */
+    void mapOverShortcuts(boolean recurse, ShortcutOperator op) {
+        ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
+        final int containerCount = containers.size();
+        for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
+            ShortcutAndWidgetContainer container = containers.get(containerIdx);
+            // map over all the shortcuts on the workspace
+            final int itemCount = container.getChildCount();
+            for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+                View item = container.getChildAt(itemIdx);
+                ItemInfo info = (ItemInfo) item.getTag();
+                if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
+                    FolderIcon folder = (FolderIcon) item;
+                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                    // map over all the children in the folder
+                    final int childCount = folderChildren.size();
+                    for (int childIdx = 0; childIdx < childCount; childIdx++) {
+                        View child = folderChildren.get(childIdx);
+                        info = (ItemInfo) child.getTag();
+                        if (op.evaluate(info, child, folder)) {
+                            return;
+                        }
+                    }
+                } else {
+                    if (op.evaluate(info, item, null)) {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
     void updateShortcuts(ArrayList<AppInfo> apps) {
         // Create a map of the apps to test against
         final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
@@ -4645,26 +4805,34 @@
             appsMap.put(ai.componentName, ai);
         }
 
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
-        for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            // Update all the children shortcuts
-            final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
-            for (int j = 0; j < layout.getChildCount(); j++) {
-                View v = layout.getChildAt(j);
-                ItemInfo info = (ItemInfo) v.getTag();
-                if (info instanceof FolderInfo && v instanceof FolderIcon) {
-                    FolderIcon folder = (FolderIcon) v;
-                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
-                    for (View fv : folderChildren) {
-                        info = (ItemInfo) fv.getTag();
-                        updateShortcut(appsMap, info, fv);
-                    }
-                    folder.invalidate();
-                } else if (info instanceof ShortcutInfo) {
+        mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (info instanceof ShortcutInfo) {
                     updateShortcut(appsMap, info, v);
+                    if (parent != null) {
+                        parent.invalidate();
+                    }
                 }
+                // process all the shortcuts
+                return false;
             }
-        }
+        });
+    }
+
+    public void updatePackageState(final String pkgName, final int state) {
+        mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (info instanceof ShortcutInfo
+                        && ((ShortcutInfo) info).isPromiseFor(pkgName)
+                        && v instanceof BubbleTextView) {
+                    ((BubbleTextView)v).setState(state);
+                }
+                // process all the shortcuts
+                return false;
+            }
+        });
     }
 
     private void moveToScreen(int page, boolean animate) {