Merge "Import translations. DO NOT MERGE" into ub-now-porkchop
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9b946af..3633c8c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,10 +23,6 @@
     <uses-sdk android:targetSdkVersion="19" android:minSdkVersion="16"/>
 
     <permission
-        android:name="com.android.launcher3.permission.PRELOAD_WORKSPACE"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem" />
-    <permission
         android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="dangerous"
@@ -76,11 +72,15 @@
 
     <application
         android:name="com.android.launcher3.LauncherApplication"
-        android:label="@string/application_name"
-        android:icon="@mipmap/ic_launcher_home"
+        android:allowBackup="@bool/enable_backup"
+        android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
         android:hardwareAccelerated="true"
+        android:icon="@mipmap/ic_launcher_home"
+        android:label="@string/application_name"
         android:largeHeap="@bool/config_largeHeap"
-        android:supportsRtl="true">
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
         <activity
             android:name="com.android.launcher3.Launcher"
             android:launchMode="singleTask"
@@ -157,15 +157,6 @@
             >
         </service>
 
-        <!-- Intent received used to prepopulate the default workspace. -->
-        <receiver
-            android:name="com.android.launcher3.PreloadReceiver"
-            android:permission="com.android.launcher3.permission.PRELOAD_WORKSPACE">
-            <intent-filter>
-                <action android:name="com.android.launcher3.action.PRELOAD_WORKSPACE" />
-            </intent-filter>
-        </receiver>
-
         <receiver
             android:name="com.android.launcher3.WallpaperChangedReceiver">
             <intent-filter>
diff --git a/WallpaperPicker/src/com/android/launcher3/Partner.java b/WallpaperPicker/src/com/android/launcher3/Partner.java
index 418ec9f..1753997 100644
--- a/WallpaperPicker/src/com/android/launcher3/Partner.java
+++ b/WallpaperPicker/src/com/android/launcher3/Partner.java
@@ -16,13 +16,9 @@
 
 package com.android.launcher3;
 
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.util.Log;
+import android.util.Pair;
 
 import java.io.File;
 
@@ -32,8 +28,6 @@
  * the system.
  */
 public class Partner {
-    private static final String TAG = "Partner";
-
     /** Marker action used to discover partner */
     private static final String
             ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
@@ -55,19 +49,9 @@
      */
     public static synchronized Partner get(PackageManager pm) {
         if (!sSearched) {
-            final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
-            for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
-                if (info.activityInfo != null &&
-                        (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    final String packageName = info.activityInfo.packageName;
-                    try {
-                        final Resources res = pm.getResourcesForApplication(packageName);
-                        sPartner = new Partner(packageName, res);
-                        break;
-                    } catch (NameNotFoundException e) {
-                        Log.w(TAG, "Failed to find resources for " + packageName);
-                    }
-                }
+            Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
+            if (apkInfo != null) {
+                sPartner = new Partner(apkInfo.first, apkInfo.second);
             }
             sSearched = true;
         }
diff --git a/res/drawable-hdpi/quantum_panel_dark.9.png b/res/drawable-hdpi/quantum_panel_dark.9.png
index 057efd1..abaf230 100644
--- a/res/drawable-hdpi/quantum_panel_dark.9.png
+++ b/res/drawable-hdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_container_holo.9.png b/res/drawable-hdpi/widget_container_holo.9.png
deleted file mode 100644
index 8c15a7c..0000000
--- a/res/drawable-hdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel_dark.9.png b/res/drawable-mdpi/quantum_panel_dark.9.png
index 7837e48..7728a72 100644
--- a/res/drawable-mdpi/quantum_panel_dark.9.png
+++ b/res/drawable-mdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_container_holo.9.png b/res/drawable-mdpi/widget_container_holo.9.png
deleted file mode 100644
index db24457..0000000
--- a/res/drawable-mdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel_dark.9.png b/res/drawable-xhdpi/quantum_panel_dark.9.png
index 8944a8f..4c1868b 100644
--- a/res/drawable-xhdpi/quantum_panel_dark.9.png
+++ b/res/drawable-xhdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_container_holo.9.png b/res/drawable-xhdpi/widget_container_holo.9.png
deleted file mode 100644
index 1313fe7..0000000
--- a/res/drawable-xhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_preloader.png b/res/drawable-xxhdpi/bg_preloader.png
deleted file mode 100644
index 56b8060..0000000
--- a/res/drawable-xxhdpi/bg_preloader.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_preloader_progress.png b/res/drawable-xxhdpi/bg_preloader_progress.png
deleted file mode 100644
index 443afe9..0000000
--- a/res/drawable-xxhdpi/bg_preloader_progress.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel_dark.9.png b/res/drawable-xxhdpi/quantum_panel_dark.9.png
index b74f1a6..17ba0f1 100644
--- a/res/drawable-xxhdpi/quantum_panel_dark.9.png
+++ b/res/drawable-xxhdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload.9.png b/res/drawable-xxhdpi/virtual_preload.9.png
new file mode 100644
index 0000000..0ec1740
--- /dev/null
+++ b/res/drawable-xxhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload_folder.9.png b/res/drawable-xxhdpi/virtual_preload_folder.9.png
new file mode 100644
index 0000000..ee80c76
--- /dev/null
+++ b/res/drawable-xxhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_container_holo.9.png b/res/drawable-xxhdpi/widget_container_holo.9.png
deleted file mode 100644
index 8f79920..0000000
--- a/res/drawable-xxhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_appwidget_not_ready.xml b/res/drawable/bg_appwidget_not_ready.xml
deleted file mode 100644
index a8b56c2..0000000
--- a/res/drawable/bg_appwidget_not_ready.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle" >
-
-    <corners android:radius="5dp" />
-
-    <solid android:color="@android:color/white" />
-
-</shape>
\ No newline at end of file
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index 03e433a..c2d399e 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -15,7 +15,8 @@
 -->
 <com.android.launcher3.AppsCustomizeTabHost
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+    android:clipChildren="false">
 
     <LinearLayout
         android:id="@+id/content"
@@ -27,15 +28,20 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_weight="1">
+            android:layout_weight="1"
+            android:clipChildren="false">
             <FrameLayout
                 android:id="@+id/fake_page_container"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent" >
+                android:layout_height="match_parent"
+                android:clipChildren="false"
+                android:clipToPadding="false">
                 <FrameLayout
-                android:id="@+id/fake_page"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent" />
+                    android:id="@+id/fake_page"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:visibility="invisible"
+                    android:clipToPadding="false" />
             </FrameLayout>
             <com.android.launcher3.AppsCustomizePagedView
                 android:id="@+id/apps_customize_pane_content"
@@ -55,13 +61,4 @@
             android:layout_height="wrap_content"
             android:layout_gravity="center" />
     </LinearLayout>
-
-    <include
-        android:id="@+id/market_button"
-        layout="@layout/market_button"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="end"
-        android:visibility="gone"/>
-
 </com.android.launcher3.AppsCustomizeTabHost>
diff --git a/res/layout/appwidget_not_ready.xml b/res/layout/appwidget_not_ready.xml
index f5f2aab..be7c33b 100644
--- a/res/layout/appwidget_not_ready.xml
+++ b/res/layout/appwidget_not_ready.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--
+     Copyright (C) 2009 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,15 +15,6 @@
      limitations under the License.
 -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<View xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingLeft="20dip"
-    android:paddingRight="20dip"
-    android:layout_margin="10dip"
-    android:gravity="center"
-    android:background="@drawable/bg_appwidget_not_ready"
-    android:textAppearance="?android:attr/textAppearanceMediumInverse"
-    android:textColor="@color/appwidget_not_ready_color"
-    android:text="@string/gadget_pending_text"
-    />
+    android:layout_height="match_parent" />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 12fa3cd..552e84c 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -152,6 +152,12 @@
         <attr name="workspace" format="reference" />
     </declare-styleable>
 
+    <declare-styleable name="PreloadIconDrawable">
+        <attr name="background" format="reference" />
+        <attr name="ringOutset" format="dimension" />
+        <attr name="indicatorSize" format="dimension" />
+    </declare-styleable>
+
     <!-- Only used in the device overlays -->
     <declare-styleable name="CustomClingTitleText">
     </declare-styleable>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 41f3892..4e64d41 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -26,7 +26,6 @@
     <color name="bubble_dark_background">#20000000</color>
 
     <color name="appwidget_error_color">#FCCC</color>
-    <color name="appwidget_not_ready_color">#F48F</color>
 
     <color name="workspace_all_apps_and_delete_zone_text_color">#CCFFFFFF</color>
     <color name="workspace_all_apps_and_delete_zone_text_shadow_color">#A0000000</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index a16f265..0da0b70 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -17,6 +17,10 @@
     <!-- Max number of page indicators to show -->
     <integer name="config_maxNumberOfPageIndicatorsToShow">21</integer>
 
+    <!-- App data backup and restore. To enble backup, register with an android backup service.
+         http://developer.android.com/guide/topics/data/backup.html#BackupKey -->
+    <bool name="enable_backup">false</bool>
+
 <!-- DragController -->
     <integer name="config_flingToDeleteMinVelocity">-1500</integer>
 
@@ -32,15 +36,19 @@
     <!-- Fade/zoom in/out duration & scale in the AllApps transition.
          Note: This should be less than the workspaceShrinkTime as they happen together. -->
     <integer name="config_appsCustomizeRevealTime">220</integer>
-    <integer name="config_appsCustomizeItemsAlphaStagger">60</integer>
     <integer name="config_appsCustomizeZoomInTime">350</integer>
     <integer name="config_appsCustomizeZoomOutTime">600</integer>
     <integer name="config_appsCustomizeZoomScaleFactor">7</integer>
     <integer name="config_appsCustomizeFadeInTime">250</integer>
     <integer name="config_appsCustomizeFadeOutTime">200</integer>
     <integer name="config_appsCustomizeWorkspaceShrinkTime">300</integer>
-    <integer name="config_appsCustomizeWorkspaceAnimationStagger">40</integer>
-    <integer name="config_workspaceAppsCustomizeAnimationStagger">100</integer>
+
+    <integer name="config_appsCustomizeConcealTime">250</integer>
+    <integer name="config_appsCustomizeItemsAlphaStagger">60</integer>
+
+    <!-- This constant stores the ratio of the all apps button drawable which
+         is used for internal (baked-in) padding -->
+    <integer name="config_allAppsButtonPaddingPercent">17</integer>
 
     <integer name="config_workspaceDefaultScreen">0</integer>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 286e04f..aa5f7b6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -193,11 +193,8 @@
     <!-- Text to show user in place of a gadget when we can't display it properly -->
     <string name="gadget_error_text">Problem loading widget</string>
 
-    <!-- Text to show user in place of a gadget when it is not yet ready. -->
-    <string name="gadget_pending_text" translatable="false">Widget not ready</string>
-
     <!-- Text to show user in place of a gadget when it is not yet initialized. -->
-    <string name="gadget_setup_text" translatable="false">Setup widget</string>
+    <string name="gadget_setup_text">Setup</string>
 
     <!-- Text to inform the user that they can't uninstall a system application -->
     <string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 462c292..6079eee 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -161,6 +161,18 @@
         <item name="android:shadowRadius">2.0</item>
     </style>
 
+    <style name="PreloadIcon">
+        <item name="background">@drawable/virtual_preload</item>
+        <item name="indicatorSize">4dp</item>
+        <item name="ringOutset">4dp</item>
+    </style>
+
+    <style name="PreloadIcon.Folder">
+        <item name="background">@drawable/virtual_preload_folder</item>
+        <item name="indicatorSize">4dp</item>
+        <item name="ringOutset">4dp</item>
+    </style>
+
     <!-- Overridden in device overlays -->
     <style name="CustomClingTitleText">
     </style>
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index e0543ce..787bf96 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -214,7 +214,7 @@
     int mWidgetLoadingId = -1;
     PendingAddWidgetInfo mCreateWidgetInfo = null;
     private boolean mDraggingWidget = false;
-    boolean mPageBackgroundsVisible;
+    boolean mPageBackgroundsVisible = true;
 
     private Toast mWidgetInstructionToast;
 
@@ -376,52 +376,19 @@
         mWidgetSpacingLayout.measure(widthSpec, heightSpec);
 
         final boolean hostIsTransitioning = getTabHost().isInTransition();
-
-        // Restore the page
         int page = getPageForComponent(mSaveInstanceStateItemIndex);
         invalidatePageData(Math.max(0, page), hostIsTransitioning);
-
-        // Show All Apps cling if we are finished transitioning, otherwise, we will try again when
-        // the transition completes in AppsCustomizeTabHost (otherwise the wrong offsets will be
-        // returned while animating)
-        if (!hostIsTransitioning) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    showAllAppsCling();
-                }
-            });
-        }
     }
 
-    void showAllAppsCling() {
-        if (!mHasShownAllAppsCling && isDataReady()) {
-            mHasShownAllAppsCling = true;
-            // Calculate the position for the cling punch through
-            int[] offset = new int[2];
-            int[] pos = mWidgetSpacingLayout.estimateCellPosition(mClingFocusedX, mClingFocusedY);
-            mLauncher.getDragLayer().getLocationInDragLayer(this, offset);
-            // PagedViews are centered horizontally but top aligned
-            // Note we have to shift the items up now that Launcher sits under the status bar
-            pos[0] += (getMeasuredWidth() - mWidgetSpacingLayout.getMeasuredWidth()) / 2 +
-                    offset[0];
-            pos[1] += offset[1] - mLauncher.getDragLayer().getPaddingTop();
-        }
-    }
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
         if (!isDataReady()) {
             if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty()) && !mWidgets.isEmpty()) {
                 setDataIsReady();
-                setMeasuredDimension(width, height);
-                onDataReady(width, height);
+                onDataReady(getMeasuredWidth(), getMeasuredHeight());
             }
         }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
     public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
@@ -1003,8 +970,8 @@
 
         Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
         if (bg != null) {
+            bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
             layout.setBackground(bg);
-            bg.setVisible(mPageBackgroundsVisible, false);
         }
 
         setVisibilityOnChildren(layout, View.VISIBLE);
@@ -1016,7 +983,7 @@
         for (int i = 0; i < childCount; ++i) {
             Drawable bg = getChildAt(i).getBackground();
             if (bg != null) {
-                bg.setVisible(visible, false);
+                bg.setAlpha(visible ? 255 : 0);
             }
         }
     }
@@ -1163,7 +1130,12 @@
         // immediately after syncing, we don't have a proper width.
         int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
         int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
-        layout.setBackground(getContext().getResources().getDrawable(R.drawable.quantum_panel_dark));
+
+        Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark);
+        if (bg != null) {
+            bg.setAlpha(mPageBackgroundsVisible ? 255 : 0);
+            layout.setBackground(bg);
+        }
         layout.measure(widthSpec, heightSpec);
     }
 
@@ -1174,11 +1146,10 @@
 
         // Calculate the dimensions of each cell we are giving to each widget
         final ArrayList<Object> items = new ArrayList<Object>();
-        int contentWidth = mContentWidth - getPaddingLeft() - getPaddingRight()
-                - layout.getPaddingLeft() - layout.getPaddingRight();
+        int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight();
         final int cellWidth = contentWidth / mWidgetCountX;
-        int contentHeight = mContentHeight - getPaddingTop() - getPaddingBottom()
-                - layout.getPaddingTop() - layout.getPaddingBottom();
+        int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom();
+
         final int cellHeight = contentHeight / mWidgetCountY;
 
         // Prepare the set of widgets to load previews for in the background
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index 629bcdb..9a516fd 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -174,7 +174,7 @@
             // Make sure the current page is loaded (we start loading the side pages after the
             // transition to prevent slowing down the animation)
             // TODO: revisit this
-            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
+            mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
         }
     }
 
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
new file mode 100644
index 0000000..4ea7b3d
--- /dev/null
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -0,0 +1,564 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Patterns;
+
+import com.android.launcher3.LauncherProvider.SqlArguments;
+import com.android.launcher3.LauncherProvider.WorkspaceLoader;
+import com.android.launcher3.LauncherSettings.Favorites;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class contains contains duplication of functionality as found in
+ * LauncherProvider#DatabaseHelper. It has been isolated and differentiated in order
+ * to cleanly and separately represent AutoInstall default layout format and policy.
+ */
+public class AutoInstallsLayout implements WorkspaceLoader {
+    private static final String TAG = "AutoInstalls";
+    private static final boolean LOGD = true;
+
+    /** Marker action used to discover a package which defines launcher customization */
+    static final String ACTION_LAUNCHER_CUSTOMIZATION =
+            "com.android.launcher3.action.LAUNCHER_CUSTOMIZATION";
+
+    private static final String LAYOUT_RES = "default_layout";
+
+    static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
+            LayoutParserCallback callback) {
+        Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
+                ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
+        if (customizationApkInfo == null) {
+            return null;
+        }
+
+        String pkg = customizationApkInfo.first;
+        Resources res = customizationApkInfo.second;
+        int layoutId = res.getIdentifier(LAYOUT_RES, "xml", pkg);
+        if (layoutId == 0) {
+            Log.e(TAG, "Layout definition not found in package: " + pkg);
+            return null;
+        }
+        return new AutoInstallsLayout(context, appWidgetHost, callback, pkg, res, layoutId);
+    }
+
+    // Object Tags
+    private static final String TAG_WORKSPACE = "workspace";
+    private static final String TAG_APP_ICON = "appicon";
+    private static final String TAG_AUTO_INSTALL = "autoinstall";
+    private static final String TAG_FOLDER = "folder";
+    private static final String TAG_APPWIDGET = "appwidget";
+    private static final String TAG_SHORTCUT = "shortcut";
+    private static final String TAG_EXTRA = "extra";
+
+    private static final String ATTR_CONTAINER = "container";
+    private static final String ATTR_RANK = "rank";
+
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_CLASS_NAME = "className";
+    private static final String ATTR_TITLE = "title";
+    private static final String ATTR_SCREEN = "screen";
+    private static final String ATTR_X = "x";
+    private static final String ATTR_Y = "y";
+    private static final String ATTR_SPAN_X = "spanX";
+    private static final String ATTR_SPAN_Y = "spanY";
+    private static final String ATTR_ICON = "icon";
+    private static final String ATTR_URL = "url";
+
+    // Style attrs -- "Extra"
+    private static final String ATTR_KEY = "key";
+    private static final String ATTR_VALUE = "value";
+
+    private static final String HOTSEAT_CONTAINER_NAME =
+            Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
+
+    private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
+            "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+
+    private final Context mContext;
+    private final AppWidgetHost mAppWidgetHost;
+    private final LayoutParserCallback mCallback;
+
+    private final PackageManager mPackageManager;
+    private final ContentValues mValues;
+
+    private final Resources mRes;
+    private final int mLayoutId;
+
+    private SQLiteDatabase mDb;
+
+    public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
+            LayoutParserCallback callback, String packageName, Resources res, int layoutId) {
+        mContext = context;
+        mAppWidgetHost = appWidgetHost;
+        mCallback = callback;
+
+        mPackageManager = context.getPackageManager();
+        mValues = new ContentValues();
+
+        mRes = res;
+        mLayoutId = layoutId;
+    }
+
+    @Override
+    public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
+        mDb = db;
+        try {
+            return parseLayout(mRes, mLayoutId, screenIds);
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            Log.w(TAG, "Got exception parsing layout.", e);
+            return -1;
+        }
+    }
+
+    private int parseLayout(Resources res, int layoutId, ArrayList<Long> screenIds)
+            throws XmlPullParserException, IOException {
+        final int hotseatAllAppsRank = LauncherAppState.getInstance()
+                .getDynamicGrid().getDeviceProfile().hotseatAllAppsRank;
+
+        XmlResourceParser parser = res.getXml(layoutId);
+        beginDocument(parser, TAG_WORKSPACE);
+        final int depth = parser.getDepth();
+        int type;
+        HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
+        int count = 0;
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            mValues.clear();
+            final int container;
+            final long screenId;
+
+            if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
+                container = Favorites.CONTAINER_HOTSEAT;
+
+                // Hack: hotseat items are stored using screen ids
+                long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
+                screenId = (rank < hotseatAllAppsRank) ? rank : (rank + 1);
+
+            } else {
+                container = Favorites.CONTAINER_DESKTOP;
+                screenId = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
+
+                mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X));
+                mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y));
+            }
+
+            mValues.put(Favorites.CONTAINER, container);
+            mValues.put(Favorites.SCREEN, screenId);
+
+            TagParser tagParser = tagParserMap.get(parser.getName());
+            if (tagParser == null) {
+                if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
+                continue;
+            }
+            long newElementId = tagParser.parseAndAdd(parser, res);
+            if (newElementId >= 0) {
+                // Keep track of the set of screens which need to be added to the db.
+                if (!screenIds.contains(screenId) &&
+                        container == Favorites.CONTAINER_DESKTOP) {
+                    screenIds.add(screenId);
+                }
+                count++;
+            }
+        }
+        return count;
+    }
+
+    protected long addShortcut(String title, Intent intent, int type) {
+        long id = mCallback.generateNewItemId();
+        mValues.put(Favorites.INTENT, intent.toUri(0));
+        mValues.put(Favorites.TITLE, title);
+        mValues.put(Favorites.ITEM_TYPE, type);
+        mValues.put(Favorites.SPANX, 1);
+        mValues.put(Favorites.SPANY, 1);
+        mValues.put(Favorites._ID, id);
+        if (mCallback.insertAndCheck(mDb, mValues) < 0) {
+            return -1;
+        } else {
+            return id;
+        }
+    }
+
+    protected HashMap<String, TagParser> getFolderElementsMap() {
+        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+        parsers.put(TAG_APP_ICON, new AppShortcutParser());
+        parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
+        parsers.put(TAG_SHORTCUT, new ShortcutParser());
+        return parsers;
+    }
+
+    protected HashMap<String, TagParser> getLayoutElementsMap() {
+        HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+        parsers.put(TAG_APP_ICON, new AppShortcutParser());
+        parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
+        parsers.put(TAG_FOLDER, new FolderParser());
+        parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+        parsers.put(TAG_SHORTCUT, new ShortcutParser());
+        return parsers;
+    }
+
+    private interface TagParser {
+        /**
+         * Parses the tag and adds to the db
+         * @return the id of the row added or -1;
+         */
+        long parseAndAdd(XmlResourceParser parser, Resources res)
+                throws XmlPullParserException, IOException;
+    }
+
+    private class AppShortcutParser implements TagParser {
+
+        @Override
+        public long parseAndAdd(XmlResourceParser parser, Resources res) {
+            final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+            final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+
+            if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+                ActivityInfo info;
+                try {
+                    ComponentName cn;
+                    try {
+                        cn = new ComponentName(packageName, className);
+                        info = mPackageManager.getActivityInfo(cn, 0);
+                    } catch (PackageManager.NameNotFoundException nnfe) {
+                        String[] packages = mPackageManager.currentToCanonicalPackageNames(
+                                new String[] { packageName });
+                        cn = new ComponentName(packages[0], className);
+                        info = mPackageManager.getActivityInfo(cn, 0);
+                    }
+                    final Intent intent = new Intent(Intent.ACTION_MAIN, null)
+                        .addCategory(Intent.CATEGORY_LAUNCHER)
+                        .setComponent(cn)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+                    return addShortcut(info.loadLabel(mPackageManager).toString(),
+                            intent, Favorites.ITEM_TYPE_APPLICATION);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
+                }
+                return -1;
+            } else {
+                if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component or uri");
+                return -1;
+            }
+        }
+    }
+
+    private class AutoInstallParser implements TagParser {
+
+        @Override
+        public long parseAndAdd(XmlResourceParser parser, Resources res) {
+            final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+            final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+            if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
+                if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+                return -1;
+            }
+
+            mValues.put(Favorites.RESTORED, 1);
+            final Intent intent = new Intent(Intent.ACTION_MAIN, null)
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setComponent(new ComponentName(packageName, className))
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            return addShortcut(mContext.getString(R.string.package_state_unknown), intent,
+                    Favorites.ITEM_TYPE_APPLICATION);
+        }
+    }
+
+    private class ShortcutParser implements TagParser {
+
+        @Override
+        public long parseAndAdd(XmlResourceParser parser, Resources res) {
+            final String url = getAttributeValue(parser, ATTR_URL);
+            final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+            final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
+
+            if (titleResId == 0 || iconId == 0) {
+                if (LOGD) Log.d(TAG, "Ignoring shortcut");
+                return -1;
+            }
+
+            if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
+                if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
+                return -1;
+            }
+            Drawable icon = res.getDrawable(iconId);
+            if (icon == null) {
+                if (LOGD) Log.d(TAG, "Ignoring shortcut, can't load icon");
+                return -1;
+            }
+
+            ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext));
+            final Intent intent = new Intent(Intent.ACTION_VIEW, null)
+                .setData(Uri.parse(url))
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+            return addShortcut(res.getString(titleResId), intent, Favorites.ITEM_TYPE_SHORTCUT);
+        }
+    }
+
+    private class AppWidgetParser implements TagParser {
+
+        @Override
+        public long parseAndAdd(XmlResourceParser parser, Resources res)
+                throws XmlPullParserException, IOException {
+            final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+            final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+            if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
+                if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+                return -1;
+            }
+
+            ComponentName cn = new ComponentName(packageName, className);
+            try {
+                mPackageManager.getReceiverInfo(cn, 0);
+            } catch (Exception e) {
+                String[] packages = mPackageManager.currentToCanonicalPackageNames(
+                        new String[] { packageName });
+                cn = new ComponentName(packages[0], className);
+                try {
+                    mPackageManager.getReceiverInfo(cn, 0);
+                } catch (Exception e1) {
+                    if (LOGD) Log.d(TAG, "Can't find widget provider: " + className);
+                    return -1;
+                }
+            }
+
+            mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
+            mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
+
+            // Read the extras
+            Bundle extras = new Bundle();
+            int widgetDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > widgetDepth) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                if (TAG_EXTRA.equals(parser.getName())) {
+                    String key = getAttributeValue(parser, ATTR_KEY);
+                    String value = getAttributeValue(parser, ATTR_VALUE);
+                    if (key != null && value != null) {
+                        extras.putString(key, value);
+                    } else {
+                        throw new RuntimeException("Widget extras must have a key and value");
+                    }
+                } else {
+                    throw new RuntimeException("Widgets can contain only extras");
+                }
+            }
+
+            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+            long insertedId = -1;
+            try {
+                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+                if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
+                    if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn);
+                    return -1;
+                }
+
+                mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
+                mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
+                mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+                mValues.put(Favorites._ID, mCallback.generateNewItemId());
+                insertedId = mCallback.insertAndCheck(mDb, mValues);
+                if (insertedId < 0) {
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                    return insertedId;
+                }
+
+                // Send a broadcast to configure the widget
+                if (!extras.isEmpty()) {
+                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
+                    intent.setComponent(cn);
+                    intent.putExtras(extras);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                    mContext.sendBroadcast(intent);
+                }
+            } catch (RuntimeException ex) {
+                if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex);
+            }
+            return insertedId;
+        }
+    }
+
+    private class FolderParser implements TagParser {
+        private final HashMap<String, TagParser> mFolderElements = getFolderElementsMap();
+
+        @Override
+        public long parseAndAdd(XmlResourceParser parser, Resources res)
+                throws XmlPullParserException, IOException {
+            final String title;
+            final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+            if (titleResId != 0) {
+                title = res.getString(titleResId);
+            } else {
+                title = mContext.getResources().getString(R.string.folder_name);
+            }
+
+            mValues.put(Favorites.TITLE, title);
+            mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
+            mValues.put(Favorites.SPANX, 1);
+            mValues.put(Favorites.SPANY, 1);
+            mValues.put(Favorites._ID, mCallback.generateNewItemId());
+            long folderId = mCallback.insertAndCheck(mDb, mValues);
+            if (folderId < 0) {
+                if (LOGD) Log.e(TAG, "Unable to add folder");
+                return -1;
+            }
+
+            final ContentValues myValues = new ContentValues(mValues);
+            ArrayList<Long> folderItems = new ArrayList<Long>();
+
+            int type;
+            int folderDepth = parser.getDepth();
+            while ((type = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > folderDepth) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                mValues.clear();
+                mValues.put(Favorites.CONTAINER, folderId);
+
+                TagParser tagParser = mFolderElements.get(parser.getName());
+                if (tagParser != null) {
+                    final long id = tagParser.parseAndAdd(parser, res);
+                    if (id >= 0) {
+                        folderItems.add(id);
+                    }
+                } else {
+                    throw new RuntimeException("Invalid folder item " + parser.getName());
+                }
+            }
+
+            long addedId = folderId;
+
+            // We can only have folders with >= 2 items, so we need to remove the
+            // folder and clean up if less than 2 items were included, or some
+            // failed to add, and less than 2 were actually added
+            if (folderItems.size() < 2) {
+                // Delete the folder
+                Uri uri = Favorites.getContentUri(folderId, false);
+                SqlArguments args = new SqlArguments(uri, null, null);
+                mDb.delete(args.table, args.where, args.args);
+                addedId = -1;
+
+                // If we have a single item, promote it to where the folder
+                // would have been.
+                if (folderItems.size() == 1) {
+                    final ContentValues childValues = new ContentValues();
+                    copyInteger(myValues, childValues, Favorites.CONTAINER);
+                    copyInteger(myValues, childValues, Favorites.SCREEN);
+                    copyInteger(myValues, childValues, Favorites.CELLX);
+                    copyInteger(myValues, childValues, Favorites.CELLY);
+
+                    addedId = folderItems.get(0);
+                    mDb.update(LauncherProvider.TABLE_FAVORITES, childValues,
+                            Favorites._ID + "=" + addedId, null);
+                }
+            }
+            return addedId;
+        }
+    }
+
+    private static final void beginDocument(XmlPullParser parser, String firstElementName)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT);
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        if (!parser.getName().equals(firstElementName)) {
+            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+                    ", expected " + firstElementName);
+        }
+    }
+
+    /**
+     * Return attribute value, attempting launcher-specific namespace first
+     * before falling back to anonymous attribute.
+     */
+    private static String getAttributeValue(XmlResourceParser parser, String attribute) {
+        String value = parser.getAttributeValue(
+                "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
+        if (value == null) {
+            value = parser.getAttributeValue(null, attribute);
+        }
+        return value;
+    }
+
+    /**
+     * Return attribute resource value, attempting launcher-specific namespace
+     * first before falling back to anonymous attribute.
+     */
+    private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+            int defaultValue) {
+        int value = parser.getAttributeResourceValue(
+                "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
+                defaultValue);
+        if (value == defaultValue) {
+            value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+        }
+        return value;
+    }
+
+    public static interface LayoutParserCallback {
+        long generateNewItemId();
+
+        long insertAndCheck(SQLiteDatabase db, ContentValues values);
+    }
+
+    private static void copyInteger(ContentValues from, ContentValues to, String key) {
+        to.put(key, from.getAsInteger(key));
+    }
+}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ab94814..5c2bb99 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -28,6 +29,7 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -39,6 +41,9 @@
  * too aggressive.
  */
 public class BubbleTextView extends TextView {
+
+    private static SparseArray<Theme> sPreloaderThemes = new SparseArray<>(2);
+
     static final float SHADOW_LARGE_RADIUS = 4.0f;
     static final float SHADOW_SMALL_RADIUS = 1.75f;
     static final float SHADOW_Y_OFFSET = 2.0f;
@@ -128,10 +133,7 @@
         LauncherAppState app = LauncherAppState.getInstance();
 
         FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b);
-        if (info.isDisabled) {
-            iconDrawable.setSaturation(0);
-            iconDrawable.setBrightness(20);
-        }
+        iconDrawable.setGhostModeEnabled(info.isDisabled);
 
         setCompoundDrawables(null, iconDrawable, null, null);
         if (setDefaultPadding) {
@@ -315,7 +317,9 @@
     }
 
     void setCellLayoutPressedOrFocusedIcon() {
-        if (getParent() instanceof ShortcutAndWidgetContainer) {
+        // Disable pressed state when the icon is in preloader state.
+        if ((getParent() instanceof ShortcutAndWidgetContainer) &&
+                !(getCompoundDrawables()[1] instanceof PreloadIconDrawable)){
             ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent();
             if (parent != null) {
                 CellLayout layout = (CellLayout) parent.getParent();
@@ -385,7 +389,13 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+
         if (mBackground != null) mBackground.setCallback(this);
+        Drawable top = getCompoundDrawables()[1];
+
+        if (top instanceof PreloadIconDrawable) {
+            ((PreloadIconDrawable) top).applyTheme(getPreloaderTheme());
+        }
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
 
@@ -466,7 +476,7 @@
                 if (top instanceof PreloadIconDrawable) {
                     preloadDrawable = (PreloadIconDrawable) top;
                 } else {
-                    preloadDrawable = new PreloadIconDrawable(top, getResources());
+                    preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme());
                     setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]);
                 }
 
@@ -478,4 +488,18 @@
             }
         }
     }
+
+    private Theme getPreloaderTheme() {
+        Object tag = getTag();
+        int style = ((tag != null) && (tag instanceof ShortcutInfo) &&
+                (((ShortcutInfo) tag).container >= 0)) ? R.style.PreloadIcon_Folder
+                        : R.style.PreloadIcon;
+        Theme theme = sPreloaderThemes.get(style);
+        if (theme == null) {
+            theme = getResources().newTheme();
+            theme.applyStyle(style, true);
+            sPreloaderThemes.put(style, theme);
+        }
+        return theme;
+    }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 018fcfc..8af2a7f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -126,6 +126,7 @@
     int searchBarSpaceHeightPx;
     int searchBarHeightPx;
     int pageIndicatorHeightPx;
+    int allAppsButtonVisualSize;
 
     float dragViewScale;
 
@@ -242,6 +243,18 @@
         // Calculate the remaining vars
         updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
         updateAvailableDimensions(context);
+        computeAllAppsButtonSize(context);
+    }
+
+    /**
+     * Determine the exact visual footprint of the all apps button, taking into account scaling
+     * and internal padding of the drawable.
+     */
+    private void computeAllAppsButtonSize(Context context) {
+        Resources res = context.getResources();
+        float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
+        LauncherAppState app = LauncherAppState.getInstance();
+        allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
     }
 
     void addCallback(DeviceProfileCallbacks cb) {
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 8bcc407..80f8dfc 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -566,6 +566,10 @@
             // the drag view about the scaled child view.
             toY += Math.round(toScale * tv.getPaddingTop());
             toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
+            if (dragView.getDragVisualizeOffset() != null) {
+                toY -=  Math.round(toScale * dragView.getDragVisualizeOffset().y);
+            }
+
             toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
         } else if (child instanceof FolderIcon) {
             // Account for holographic blur padding on the drag view
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index ef8d097..cf7c22e 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -28,15 +28,17 @@
 
 class FastBitmapDrawable extends Drawable {
 
-    private static final ColorMatrix sTempSaturationMatrix = new ColorMatrix();
-    private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix();
+    private static ColorMatrix sGhostModeMatrix;
+    private static final ColorMatrix sTempMatrix = new ColorMatrix();
+
+    private static final int GHOST_MODE_MIN_COLOR_RANGE = 130;
 
     private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
     private Bitmap mBitmap;
     private int mAlpha;
 
-    private float mSatutation = 1;
     private int mBrightness = 0;
+    private boolean mGhostModeEnabled = false;
 
     FastBitmapDrawable(Bitmap b) {
         mAlpha = 255;
@@ -101,13 +103,19 @@
         return mBitmap;
     }
 
-    public float getSaturation() {
-        return mSatutation;
+    /**
+     * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost'
+     * appearance.
+     */
+    public void setGhostModeEnabled(boolean enabled) {
+        if (mGhostModeEnabled != enabled) {
+            mGhostModeEnabled = enabled;
+            updateFilter();
+        }
     }
 
-    public void setSaturation(float saturation) {
-        mSatutation = saturation;
-        updateFilter();
+    public boolean isGhostModeEnabled() {
+        return mGhostModeEnabled;
     }
 
     public int getBrightness() {
@@ -115,36 +123,58 @@
     }
 
     public void addBrightness(int amount) {
-        mBrightness += amount;
-        updateFilter();
+        setBrightness(mBrightness + amount);
     }
 
     public void setBrightness(int brightness) {
-        mBrightness = brightness;
-        updateFilter();
+        if (mBrightness != brightness) {
+            mBrightness = brightness;
+            updateFilter();
+        }
     }
 
     private void updateFilter() {
-        if (mSatutation != 1 || mBrightness != 0) {
-            sTempSaturationMatrix.setSaturation(mSatutation);
+        if (mGhostModeEnabled) {
+            if (sGhostModeMatrix == null) {
+                sGhostModeMatrix = new ColorMatrix();
+                sGhostModeMatrix.setSaturation(0);
 
-            if (mBrightness != 0) {
-                // Brightness: C-new = C-old*(1-amount) + amount
-                float scale = 1 - mBrightness / 255.0f;
-                sTempBrightnessMatrix.setScale(scale, scale, scale, 1);
-                float[] array = sTempBrightnessMatrix.getArray();
-
-                // Add the amount to RGB components of the matrix, as per the above formula.
-                // Fifth elements in the array correspond to the constant being added to
-                // red, blue, green, and alpha channel respectively.
-                array[4] = mBrightness;
-                array[9] = mBrightness;
-                array[14] = mBrightness;
-                sTempSaturationMatrix.preConcat(sTempBrightnessMatrix);
+                // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255]
+                float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f;
+                sTempMatrix.set(new float[] {
+                        range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
+                        0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
+                        0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE,
+                        0, 0, 0, 1, 0 });
+                sGhostModeMatrix.preConcat(sTempMatrix);
             }
-            mPaint.setColorFilter(new ColorMatrixColorFilter(sTempSaturationMatrix));
+
+            if (mBrightness == 0) {
+                mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix));
+            } else {
+                setBrightnessMatrix(sTempMatrix, mBrightness);
+                sTempMatrix.postConcat(sGhostModeMatrix);
+                mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
+            }
+        } else if (mBrightness != 0) {
+            setBrightnessMatrix(sTempMatrix, mBrightness);
+            mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
         } else {
             mPaint.setColorFilter(null);
         }
     }
+
+    private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) {
+        // Brightness: C-new = C-old*(1-amount) + amount
+        float scale = 1 - brightness / 255.0f;
+        matrix.setScale(scale, scale, scale, 1);
+        float[] array = matrix.getArray();
+
+        // Add the amount to RGB components of the matrix, as per the above formula.
+        // Fifth elements in the array correspond to the constant being added to
+        // red, blue, green, and alpha channel respectively.
+        array[4] = brightness;
+        array[9] = brightness;
+        array[14] = brightness;
+    }
 }
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b1250ce..df5e0fc 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -57,16 +57,6 @@
     }
 }
 
-/**
- * A keyboard listener we set on the last tab button in AppsCustomize to jump to then
- * market icon and vice versa.
- */
-class AppsCustomizeTabKeyEventListener implements View.OnKeyListener {
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        return FocusHelper.handleAppsCustomizeTabKeyEvent(v, keyCode, event);
-    }
-}
-
 public class FocusHelper {
     /**
      * Private helper to get the parent TabHost in the view hiearchy.
@@ -80,41 +70,6 @@
     }
 
     /**
-     * Handles key events in a AppsCustomize tab between the last tab view and the shop button.
-     */
-    static boolean handleAppsCustomizeTabKeyEvent(View v, int keyCode, KeyEvent e) {
-        final AppsCustomizeTabHost tabHost = findTabHostParent(v);
-        final ViewGroup contents = tabHost.getContent();
-        final View shop = tabHost.findViewById(R.id.market_button);
-
-        final int action = e.getAction();
-        final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
-        boolean wasHandled = false;
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                if (handleKeyEvent) {
-                    // Select the shop button if we aren't on it
-                    if (v != shop) {
-                        shop.requestFocus();
-                    }
-                }
-                wasHandled = true;
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (handleKeyEvent) {
-                    // Select the content view (down is handled by the tab key handler otherwise)
-                    if (v == shop) {
-                        contents.requestFocus();
-                        wasHandled = true;
-                    }
-                }
-                break;
-            default: break;
-        }
-        return wasHandled;
-    }
-
-    /**
      * Returns the Viewgroup containing page contents for the page at the index specified.
      */
     private static ViewGroup getAppsCustomizePage(ViewGroup container, int index) {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 655e5c3..fcedaea 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -1182,6 +1182,18 @@
         if (mIsExternalDrag) {
             si.cellX = mEmptyCell[0];
             si.cellY = mEmptyCell[1];
+
+            // Actually move the item in the database if it was an external drag. Call this
+            // before creating the view, so that ShortcutInfo is updated appropriately.
+            LauncherModel.addOrMoveItemInDatabase(
+                    mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+
+            // We only need to update the locations if it doesn't get handled in #onDropCompleted.
+            if (d.dragSource != this) {
+                updateItemLocationsInDatabaseBatch();
+            }
+            mIsExternalDrag = false;
+
             currentDragView = createAndAddShortcut(si);
         } else {
             currentDragView = mCurrentDragView;
@@ -1209,18 +1221,6 @@
         mItemsInvalidated = true;
         setupContentDimensions(getItemCount());
 
-        // Actually move the item in the database if it was an external drag.
-        if (mIsExternalDrag) {
-            LauncherModel.addOrMoveItemInDatabase(
-                    mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
-
-            // We only need to update the locations if it doesn't get handled in #onDropCompleted.
-            if (d.dragSource != this) {
-                updateItemLocationsInDatabaseBatch();
-            }
-            mIsExternalDrag = false;
-        }
-
         // Temporarily suppress the listener, as we did all the work already here.
         mSuppressOnAdd = true;
         mInfo.add(si);
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 5b49fb8..c0b9da7 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -380,7 +380,7 @@
             float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
 
         // These correspond two the drawable and view that the icon was dropped _onto_
-        Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
+        Drawable animateDrawable = getTopDrawable((TextView) destView);
         computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                 destView.getMeasuredWidth());
 
@@ -394,7 +394,7 @@
     }
 
     public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
-        Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
+        Drawable animateDrawable = getTopDrawable((TextView) finalView);
         computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                 finalView.getMeasuredWidth());
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 221df58..06b7775 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -16,22 +16,20 @@
 
 package com.android.launcher3;
 
-import com.android.launcher3.backup.BackupProtos;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
@@ -54,12 +52,15 @@
  * Cache of application icons.  Icons can be made from any thread.
  */
 public class IconCache {
-    @SuppressWarnings("unused")
+
     private static final String TAG = "Launcher.IconCache";
 
     private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
     private static final String RESOURCE_FILE_PREFIX = "icon_";
 
+    // Empty class name is used for storing package default entry.
+    private static final String EMPTY_CLASS_NAME = ".";
+
     private static final boolean DEBUG = true;
 
     private static class CacheEntry {
@@ -237,7 +238,7 @@
             HashMap<Object, CharSequence> labelCache) {
         synchronized (mCache) {
             CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
-                    info.getUser());
+                    info.getUser(), false);
 
             application.title = entry.title;
             application.iconBitmap = entry.icon;
@@ -246,10 +247,10 @@
     }
 
     public Bitmap getIcon(Intent intent, UserHandleCompat user) {
-        return getIcon(intent, null, user);
+        return getIcon(intent, null, user, true);
     }
 
-    public Bitmap getIcon(Intent intent, String title, UserHandleCompat user) {
+    public Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
         synchronized (mCache) {
             LauncherActivityInfoCompat launcherActInfo =
                     mLauncherApps.resolveActivity(intent, user);
@@ -257,11 +258,11 @@
 
             // null info means not installed, but if we have a component from the intent then
             // we should still look in the cache for restored app icons.
-            if (launcherActInfo == null && component == null) {
+            if (component == null) {
                 return getDefaultIcon(user);
             }
 
-            CacheEntry entry = cacheLocked(component, launcherActInfo, null, user);
+            CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
             if (title != null) {
                 entry.title = title;
                 entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
@@ -284,7 +285,7 @@
                 return null;
             }
 
-            CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser());
+            CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
             return entry.icon;
         }
     }
@@ -294,7 +295,7 @@
     }
 
     private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
-            HashMap<Object, CharSequence> labelCache, UserHandleCompat user) {
+            HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
         CacheKey cacheKey = new CacheKey(componentName, user);
         CacheEntry entry = mCache.get(cacheKey);
         if (entry == null) {
@@ -324,15 +325,52 @@
                             componentName.toShortString());
                     entry.icon = preloaded;
                 } else {
-                    if (DEBUG) Log.d(TAG, "using default icon for " +
-                            componentName.toShortString());
-                    entry.icon = getDefaultIcon(user);
+                    if (usePackageIcon) {
+                        CacheEntry packageEntry = getEntryForPackage(
+                                componentName.getPackageName(), user);
+                        if (packageEntry != null && packageEntry.icon != null) {
+                            if (DEBUG) Log.d(TAG, "using package default icon for " +
+                                    componentName.toShortString());
+                            entry.icon = packageEntry.icon;
+                        }
+                    }
+                    if (entry.icon == null) {
+                        if (DEBUG) Log.d(TAG, "using default icon for " +
+                                componentName.toShortString());
+                        entry.icon = getDefaultIcon(user);
+                    }
                 }
             }
         }
         return entry;
     }
 
+    /**
+     * Gets an entry for the package, which can be used as a fallback entry for various components.
+     */
+    private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
+        ComponentName cn = getPackageComponent(packageName);
+        CacheKey cacheKey = new CacheKey(cn, user);
+        CacheEntry entry = mCache.get(cacheKey);
+        if (entry == null) {
+            entry = new CacheEntry();
+            mCache.put(cacheKey, entry);
+
+            try {
+                ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
+                entry.title = info.loadLabel(mPackageManager);
+                entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
+            } catch (NameNotFoundException e) {
+                if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
+            }
+
+            if (entry.icon == null) {
+                entry.icon = getPreloadedIcon(cn, user);
+            }
+        }
+        return entry;
+    }
+
     public HashMap<ComponentName,Bitmap> getAllIcons() {
         synchronized (mCache) {
             HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
@@ -471,4 +509,8 @@
         String filename = resourceName.replace(File.separatorChar, '_');
         return RESOURCE_FILE_PREFIX + filename;
     }
+
+    static ComponentName getPackageComponent(String packageName) {
+        return new ComponentName(packageName, EMPTY_CLASS_NAME);
+    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5eedc8a..8995856 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -23,7 +23,6 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -90,10 +89,8 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Advanceable;
 import android.widget.FrameLayout;
@@ -245,6 +242,7 @@
     private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     private static int NEW_APPS_ANIMATION_DELAY = 500;
+    private static final int SINGLE_FRAME_DELAY = 16;
 
     private final BroadcastReceiver mCloseSystemDialogsReceiver
             = new CloseSystemDialogsIntentReceiver();
@@ -331,10 +329,6 @@
     // External icons saved in case of resource changes, orientation, etc.
     private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
     private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
-    private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
-
-    private Intent mAppMarketIntent = null;
-    private static final boolean DISABLE_MARKET_BUTTON = true;
 
     private Drawable mWorkspaceBackgroundDrawable;
 
@@ -458,7 +452,6 @@
                     Environment.getExternalStorageDirectory() + "/launcher");
         }
 
-
         checkForLocaleChange();
         setContentView(R.layout.launcher);
 
@@ -560,11 +553,7 @@
         boolean voiceVisible = false;
         // If we have a saved version of these external icons, we load them up immediately
         int coi = getCurrentOrientationIndexForGlobalIcons();
-        if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
-                sAppMarketIcon[coi] == null) {
-            if (!DISABLE_MARKET_BUTTON) {
-                updateAppMarketIcon();
-            }
+        if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null) {
             searchVisible = updateGlobalSearchIcon();
             voiceVisible = updateVoiceSearchIcon(searchVisible);
         }
@@ -576,9 +565,6 @@
             updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
             voiceVisible = true;
         }
-        if (!DISABLE_MARKET_BUTTON && sAppMarketIcon[coi] != null) {
-            updateAppMarketIcon(sAppMarketIcon[coi]);
-        }
         if (mSearchDropTargetBar != null) {
             mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
         }
@@ -1756,11 +1742,6 @@
                     }
                 });
             }
-            // When Launcher comes back to foreground, a different Activity might be responsible for
-            // the app market intent, so refresh the icon
-            if (!DISABLE_MARKET_BUTTON) {
-                updateAppMarketIcon();
-            }
             clearTypedText();
         }
     }
@@ -2742,16 +2723,6 @@
         return mHapticFeedbackTouchListener;
     }
 
-    public void onClickAppMarketButton(View v) {
-        if (!DISABLE_MARKET_BUTTON) {
-            if (mAppMarketIntent != null) {
-                startActivitySafely(v, mAppMarketIntent, "app market");
-            } else {
-                Log.e(TAG, "Invalid app market intent.");
-            }
-        }
-    }
-
     public void onDragStarted(View view) {}
 
     /**
@@ -3226,8 +3197,6 @@
         final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mWorkspace;
         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
-        final int startDelay =
-                res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
 
         Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
                 Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
@@ -3248,7 +3217,10 @@
             final View page = content.getPageAt(content.getCurrentPage());
             final View revealView = toView.findViewById(R.id.fake_page);
 
-            if (contentType == AppsCustomizePagedView.ContentType.Widgets) {
+            final float initialPanelAlpha = 1f;
+
+            final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
+            if (isWidgetTray) {
                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
             } else {
                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
@@ -3262,15 +3234,21 @@
             int height = revealView.getMeasuredHeight();
 
             float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
-            float yDrift = height / 2f;
+            revealView.setTranslationY(0);
 
+            // Get the y delta between the center of the page and the center of the all apps button
+            int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+                    getAllAppsButton(), null);
+            float yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
+
+            float initAlpha = isWidgetTray ? 0.3f : 1f;
             revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", 0.4f, 1f);
+            PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
             PropertyValuesHolder panelDrift =
-                    PropertyValuesHolder.ofFloat("translationY", yDrift, 0f);
+                    PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
 
             ObjectAnimator panelAlphaAndDrift =
-                    ObjectAnimator.ofPropertyValuesHolder(revealView, panelAlpha, panelDrift);
+                    LauncherAnimUtils.ofPropertyValuesHolder(revealView, panelAlpha, panelDrift);
             panelAlphaAndDrift.setDuration(revealDuration);
             panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
 
@@ -3280,15 +3258,14 @@
                 page.setVisibility(View.VISIBLE);
                 page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
 
-                ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
+                ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY", yDrift, 0);
                 pageDrift.setDuration(revealDuration);
                 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+                pageDrift.setStartDelay(itemsAlphaStagger);
                 mStateAnimation.play(pageDrift);
 
-                // TODO (adamcohen): remove this 0.01f hack once fw is fixed
-                // it's there to work around a framework bug (16918357)
-                page.setAlpha(0.01f);
-                ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0.01f, 1f);
+                page.setAlpha(0f);
+                ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 0f, 1f);
                 itemsAlpha.setDuration(revealDuration);
                 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
                 itemsAlpha.setStartDelay(itemsAlphaStagger);
@@ -3298,16 +3275,34 @@
             View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
             pageIndicators.setAlpha(0.01f);
             ObjectAnimator indicatorsAlpha =
-                    ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
+                    LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 1f);
             indicatorsAlpha.setDuration(revealDuration);
             mStateAnimation.play(indicatorsAlpha);
 
             if (material) {
+                final View allApps = getAllAppsButton();
+                int allAppsButtonSize = LauncherAppState.getInstance().
+                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
                 ValueAnimator reveal = (ValueAnimator)
-                        ViewAnimationUtils.createCircularReveal(revealView, width / 2,
-                                height / 2, 0f, revealRadius);
+                        LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+                                height / 2, startRadius, revealRadius);
                 reveal.setDuration(revealDuration);
                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+                reveal.addListener(new AnimatorListenerAdapter() {
+                    public void onAnimationStart(Animator animation) {
+                        if (!isWidgetTray) {
+                            allApps.setVisibility(View.INVISIBLE);
+                        }
+                    }
+                    public void onAnimationEnd(Animator animation) {
+                        if (!isWidgetTray) {
+                            allApps.setVisibility(View.VISIBLE);
+                        }
+                    }
+                });
+
                 mStateAnimation.play(reveal);
             }
 
@@ -3416,12 +3411,11 @@
         }
 
         boolean material = Utilities.isLmp();
-
         Resources res = getResources();
 
         final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
         final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
-        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+        final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
 
@@ -3431,9 +3425,8 @@
         final View toView = mWorkspace;
         Animator workspaceAnim = null;
         if (toState == Workspace.State.NORMAL) {
-            int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
             workspaceAnim = mWorkspace.getChangeStateAnimation(
-                    toState, animated, stagger, -1);
+                    toState, animated);
         } else if (toState == Workspace.State.SPRING_LOADED ||
                 toState == Workspace.State.OVERVIEW) {
             workspaceAnim = mWorkspace.getChangeStateAnimation(
@@ -3451,7 +3444,9 @@
             final View revealView = fromView.findViewById(R.id.fake_page);
 
             AppsCustomizePagedView.ContentType contentType = content.getContentType();
-            if (contentType == AppsCustomizePagedView.ContentType.Widgets) {
+            final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
+
+            if (isWidgetTray) {
                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
             } else {
                 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
@@ -3465,34 +3460,43 @@
             revealView.setVisibility(View.VISIBLE);
             content.setPageBackgroundsVisible(false);
 
-            float yDrift = height / 2f;
+            final View allAppsButton = getAllAppsButton();
+            revealView.setTranslationY(0);
+            int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+                    allAppsButton, null);
+            float yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
 
             revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
 
-            PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f);
-            PropertyValuesHolder panelDrift =
-                    PropertyValuesHolder.ofFloat("translationY", 0, yDrift);
-            ObjectAnimator panelAlphaAndDrift =
-                    ObjectAnimator.ofPropertyValuesHolder(revealView, panelAlpha, panelDrift);
-            panelAlphaAndDrift.setDuration(revealDuration);
+            // The vertical motion of the apps panel should be delayed by one frame
+            // from the conceal animation in order to give the right feel. We correpsondingly
+            // shorten the duration so that the slide and conceal end at the same time.
+            ObjectAnimator panelDrift = LauncherAnimUtils.ofFloat(revealView, "translationY", 0, yDrift);
+            panelDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+            panelDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+            panelDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+            mStateAnimation.play(panelDrift);
 
-            panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-            panelAlphaAndDrift.setStartDelay(itemsAlphaStagger);
-
-            mStateAnimation.play(panelAlphaAndDrift);
+            if (isWidgetTray) {
+                ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha", 1f, 0.4f);
+                panelAlpha.setDuration(revealDuration);
+                panelAlpha.setInterpolator(new LogDecelerateInterpolator(100, 0));
+                mStateAnimation.play(panelAlpha);
+            }
 
             if (page != null) {
                 page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
 
-                ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", 0, yDrift);
-                pageDrift.setDuration(revealDuration);
+                ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
+                        0, yDrift);
+                pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
                 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-                pageDrift.setStartDelay(itemsAlphaStagger);
+                pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
                 mStateAnimation.play(pageDrift);
 
                 page.setAlpha(1f);
-                ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, View.ALPHA, 1f, 0f);
-                itemsAlpha.setDuration(revealDuration);
+                ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
+                itemsAlpha.setDuration(100);
                 itemsAlpha.setInterpolator(new LogDecelerateInterpolator(100, 0));
                 mStateAnimation.play(itemsAlpha);
             }
@@ -3500,7 +3504,7 @@
             View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
             pageIndicators.setAlpha(1f);
             ObjectAnimator indicatorsAlpha =
-                    ObjectAnimator.ofFloat(pageIndicators, "alpha", 0f);
+                    LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
             indicatorsAlpha.setDuration(revealDuration);
             indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
             mStateAnimation.play(indicatorsAlpha);
@@ -3508,9 +3512,15 @@
             width = revealView.getMeasuredWidth();
 
             if (material) {
+                if (!isWidgetTray) {
+                    allAppsButton.setVisibility(View.INVISIBLE);
+                }
+                int allAppsButtonSize = LauncherAppState.getInstance().
+                        getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+                float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
                 Animator reveal =
-                        ViewAnimationUtils.createCircularReveal(revealView, width / 2,
-                                height / 2 + 100, revealRadius, 0f);
+                        LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+                                height / 2, revealRadius, finalRadius);
                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
                 reveal.setDuration(revealDuration);
                 reveal.setStartDelay(itemsAlphaStagger);
@@ -3518,6 +3528,9 @@
                 reveal.addListener(new AnimatorListenerAdapter() {
                     public void onAnimationEnd(Animator animation) {
                         revealView.setVisibility(View.INVISIBLE);
+                        if (!isWidgetTray) {
+                            allAppsButton.setVisibility(View.VISIBLE);
+                        }
                     }
                 });
 
@@ -3969,44 +3982,6 @@
     public void disableVoiceButtonProxy(boolean disabled) {
         updateVoiceButtonProxyVisible(disabled);
     }
-    /**
-     * Sets the app market icon
-     */
-    private void updateAppMarketIcon() {
-        if (!DISABLE_MARKET_BUTTON) {
-            final View marketButton = findViewById(R.id.market_button);
-            Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
-            // Find the app market activity by resolving an intent.
-            // (If multiple app markets are installed, it will return the ResolverActivity.)
-            ComponentName activityName = intent.resolveActivity(getPackageManager());
-            if (activityName != null) {
-                int coi = getCurrentOrientationIndexForGlobalIcons();
-                mAppMarketIntent = intent;
-                sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
-                        R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
-                        TOOLBAR_ICON_METADATA_NAME);
-                marketButton.setVisibility(View.VISIBLE);
-            } else {
-                // We should hide and disable the view so that we don't try and restore the visibility
-                // of it when we swap between drag & normal states from IconDropTarget subclasses.
-                marketButton.setVisibility(View.GONE);
-                marketButton.setEnabled(false);
-            }
-        }
-    }
-
-    private void updateAppMarketIcon(Drawable.ConstantState d) {
-        if (!DISABLE_MARKET_BUTTON) {
-            // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
-            Resources r = getResources();
-            Drawable marketIconDrawable = d.newDrawable(r);
-            int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
-            int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
-            marketIconDrawable.setBounds(0, 0, w, h);
-
-            updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
-        }
-    }
 
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
@@ -4445,7 +4420,9 @@
             item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
         } else {
             appWidgetInfo = null;
-            item.hostView = new PendingAppWidgetHostView(this, item.restoreStatus);
+            PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item);
+            view.updateIcon(mIconCache);
+            item.hostView = view;
             item.hostView.updateAppWidget(null);
             item.hostView.setOnClickListener(this);
         }
@@ -4478,10 +4455,7 @@
             return;
         }
 
-        PendingAppWidgetHostView pendingView = (PendingAppWidgetHostView) view;
-        pendingView.setStatus(LauncherAppWidgetInfo.RESTORE_COMPLETED);
-
-        LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) pendingView.getTag();
+        LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
         info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
 
         mWorkspace.reinflateWidgetsIfNecessary();
@@ -4515,12 +4489,6 @@
 
         mWorkspace.restoreInstanceStateForRemainingPages();
 
-        // Update the market app icon as necessary (the other icons will be managed in response to
-        // package changes in bindSearchablesChanged()
-        if (!DISABLE_MARKET_BUTTON) {
-            updateAppMarketIcon();
-        }
-
         setWorkspaceLoading(false);
         sendLoadingCompleteBroadcastIfNecessary();
 
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index e6c220b..be295f8 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -22,6 +22,7 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.view.View;
+import android.view.ViewAnimationUtils;
 import android.view.ViewTreeObserver;
 
 import java.util.HashSet;
@@ -126,4 +127,14 @@
         new FirstFrameAnimatorHelper(anim, view);
         return anim;
     }
+
+    public static Animator createCircularReveal(View view, int centerX,
+            int centerY, float startRadius, float endRadius) {
+        Animator anim = ViewAnimationUtils.createCircularReveal(view, centerX,
+                centerY, startRadius, endRadius);
+        if (anim instanceof ValueAnimator) {
+            new FirstFrameAnimatorHelper((ValueAnimator) anim, view);
+        }
+        return anim;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index fa5e38f..a309f26 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -22,6 +22,8 @@
 import android.content.Context;
 import android.os.TransactionTooLargeException;
 
+import java.util.ArrayList;
+
 /**
  * Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
  * which correctly captures all long-press events. This ensures that users can
@@ -29,6 +31,8 @@
  */
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
+    private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+
     Launcher mLauncher;
 
     public LauncherAppWidgetHost(Launcher launcher, int hostId) {
@@ -64,9 +68,21 @@
         clearViews();
     }
 
+    public void addProviderChangeListener(Runnable callback) {
+        mProviderChangeListeners.add(callback);
+    }
+
+    public void removeProviderChangeListener(Runnable callback) {
+        mProviderChangeListeners.remove(callback);
+    }
+
     protected void onProvidersChanged() {
         // Once we get the message that widget packages are updated, we need to rebind items
         // in AppsCustomize accordingly.
         mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher));
+
+        for (Runnable callback : mProviderChangeListeners) {
+            callback.run();
+        }
     }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index c1535ab..4755482 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -67,6 +67,11 @@
      */
     int restoreStatus;
 
+    /**
+     * Indicates the installation progress of the widget provider
+     */
+    int installProgress;
+
     private boolean mHasNotifiedInitialWidgetSizeChanged;
 
     /**
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index aecf9b0..64e82c7 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -692,6 +692,9 @@
                         .decodeByteArray(widget.icon.data, 0, widget.icon.data.length);
                 if (icon == null) {
                     Log.w(TAG, "failed to unpack widget icon for " + key.name);
+                } else {
+                    IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider),
+                            icon, widget.icon.dpi);
                 }
             }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5c668d6..4c9d1a7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1860,7 +1860,7 @@
             } else {
                 // Make sure the default workspace is loaded
                 Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
-                LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
+                LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
             }
 
             // This code path is for our old migration code and should no longer be exercised
@@ -3089,7 +3089,7 @@
         info.user = UserHandleCompat.myUserHandle();
         info.contentDescription = mUserManager.getBadgedLabelForUser(
                 info.title.toString(), info.user);
-        info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
+        info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user, false));
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
         info.restoredIntent = intent;
         info.wasPromise = true;
@@ -3378,7 +3378,7 @@
     /**
      * Attempts to find an AppWidgetProviderInfo that matches the given component.
      */
-    AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
+    static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
             ComponentName component) {
         List<AppWidgetProviderInfo> widgets =
             AppWidgetManager.getInstance(context).getInstalledProviders();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 88ea45a..af65536 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -33,11 +33,9 @@
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 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;
 import android.database.Cursor;
 import android.database.SQLException;
@@ -51,15 +49,14 @@
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.Xml;
 
+import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.LauncherSettings.Favorites;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -93,8 +90,6 @@
             "UPGRADED_FROM_OLD_DATABASE";
     static final String EMPTY_DATABASE_CREATED =
             "EMPTY_DATABASE_CREATED";
-    static final String DEFAULT_WORKSPACE_RESOURCE_ID =
-            "DEFAULT_WORKSPACE_RESOURCE_ID";
 
     private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
             "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
@@ -313,41 +308,41 @@
     }
 
     /**
-     * @param workspaceResId that can be 0 to use default or non-zero for specific resource
+     * Loads the default workspace based on the following priority scheme:
+     *   1) From a package provided by play store
+     *   2) From a partner configuration APK, already in the system image
+     *   3) The default configuration for the particular device
      */
-    synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
+    synchronized public void loadDefaultFavoritesIfNecessary() {
         String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
         if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
             Log.d(TAG, "loading default workspace");
-            // By default we use our resources
-            Resources res = getContext().getResources();
-            int workspaceResId = origWorkspaceResId;
 
-            // Use default workspace resource if none provided
-            if (workspaceResId == 0) {
+            WorkspaceLoader loader = AutoInstallsLayout.get(getContext(),
+                    mOpenHelper.mAppWidgetHost, mOpenHelper);
+
+            if (loader == null) {
                 final Partner partner = Partner.get(getContext().getPackageManager());
                 if (partner != null && partner.hasDefaultLayout()) {
                     final Resources partnerRes = partner.getResources();
-                    workspaceResId = partnerRes.getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
+                    int workspaceResId = partnerRes.getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
                             "xml", partner.getPackageName());
-                    res = partnerRes;
+                    if (workspaceResId != 0) {
+                        loader = new SimpleWorkspaceLoader(mOpenHelper, partnerRes, workspaceResId);
+                    }
                 }
             }
-            if (workspaceResId == 0) {
-                workspaceResId =
-                        sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
+
+            if (loader == null) {
+                loader = new SimpleWorkspaceLoader(mOpenHelper, getContext().getResources(),
+                        getDefaultWorkspaceResourceId());
             }
 
             // Populate favorites table with initial favorites
-            SharedPreferences.Editor editor = sp.edit();
-            editor.remove(EMPTY_DATABASE_CREATED);
-            if (origWorkspaceResId != 0) {
-                editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
-            }
-
-            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), res, workspaceResId);
+            SharedPreferences.Editor editor = sp.edit().remove(EMPTY_DATABASE_CREATED);
+            mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader);
             editor.commit();
         }
     }
@@ -389,12 +384,10 @@
         mOpenHelper = new DatabaseHelper(getContext());
     }
 
-    private static class DatabaseHelper extends SQLiteOpenHelper {
+    private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
         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";
-        private static final String TAG_SEARCH = "search";
         private static final String TAG_APPWIDGET = "appwidget";
         private static final String TAG_SHORTCUT = "shortcut";
         private static final String TAG_FOLDER = "folder";
@@ -789,7 +782,8 @@
                 }
 
                 // Add default hotseat icons
-                loadFavorites(db, mContext.getResources(), R.xml.update_workspace);
+                loadFavorites(db, new SimpleWorkspaceLoader(this, mContext.getResources(),
+                        R.xml.update_workspace));
                 version = 9;
             }
 
@@ -1084,6 +1078,7 @@
         // constructor from the worker thread; however, this doesn't extend until after the
         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
         // after that point
+        @Override
         public long generateNewItemId() {
             if (mMaxItemId < 0) {
                 throw new RuntimeException("Error: max item id was not initialized");
@@ -1092,6 +1087,11 @@
             return mMaxItemId;
         }
 
+        @Override
+        public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+            return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
+        }
+
         public void updateMaxItemId(long id) {
             mMaxItemId = id + 1;
         }
@@ -1365,9 +1365,10 @@
             return intent;
         }
 
-        private int loadFavorites(SQLiteDatabase db, Resources res, int workspaceResourceId) {
+        private int loadFavorites(SQLiteDatabase db, WorkspaceLoader loader) {
             ArrayList<Long> screenIds = new ArrayList<Long>();
-            int count = loadFavoritesRecursive(db, res, workspaceResourceId, screenIds);
+            // TODO: Use multiple loaders with fall-back and transaction.
+            int count = loader.loadLayout(db, screenIds);
 
             // Add the screens specified by the items above
             Collections.sort(screenIds);
@@ -1922,7 +1923,7 @@
             return id;
         }
 
-        public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
+        private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
             final ContentResolver resolver = mContext.getContentResolver();
             Cursor c = null;
             int count = 0;
@@ -1977,7 +1978,6 @@
                         final int width = (int) grid.numColumns;
                         final int height = (int) grid.numRows;
                         final int hotseatWidth = (int) grid.numHotseatIcons;
-                        PackageManager pm = mContext.getPackageManager();
 
                         final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
 
@@ -2211,7 +2211,7 @@
      * Build a query string that will match any row where the column matches
      * anything in the values list.
      */
-    static String buildOrWhereString(String column, int[] values) {
+    private static String buildOrWhereString(String column, int[] values) {
         StringBuilder selectWhere = new StringBuilder();
         for (int i = values.length - 1; i >= 0; i--) {
             selectWhere.append(column).append("=").append(values[i]);
@@ -2226,7 +2226,7 @@
      * Return attribute value, attempting launcher-specific namespace first
      * before falling back to anonymous attribute.
      */
-    static String getAttributeValue(XmlResourceParser parser, String attribute) {
+    private static String getAttributeValue(XmlResourceParser parser, String attribute) {
         String value = parser.getAttributeValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
         if (value == null) {
@@ -2239,7 +2239,7 @@
      * Return attribute resource value, attempting launcher-specific namespace
      * first before falling back to anonymous attribute.
      */
-    static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+    private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
             int defaultValue) {
         int value = parser.getAttributeResourceValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
@@ -2285,4 +2285,29 @@
             }
         }
     }
+
+    static interface WorkspaceLoader {
+        /**
+         * @param screenIds A mutable list of screen its
+         * @return the number of workspace items added.
+         */
+        int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds);
+    }
+
+    private static class SimpleWorkspaceLoader implements WorkspaceLoader {
+        private final Resources mRes;
+        private final int mWorkspaceId;
+        private final DatabaseHelper mHelper;
+
+        SimpleWorkspaceLoader(DatabaseHelper helper, Resources res, int workspaceId) {
+            mHelper = helper;
+            mRes = res;
+            mWorkspaceId = workspaceId;
+        }
+
+        @Override
+        public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
+            return mHelper.loadFavoritesRecursive(db, mRes, mWorkspaceId, screenIds);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 90a6b15..cd0d550 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1159,7 +1159,7 @@
     }
 
     protected boolean shouldDrawChild(View child) {
-        return child.getAlpha() > 0 && child.getVisibility() == VISIBLE;
+        return child.getVisibility() == VISIBLE;
     }
 
     @Override
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 048e9f8..0401436 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -1,21 +1,59 @@
+/*
+ * 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.
+ */
+
 package com.android.launcher3;
 
 import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources.Theme;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.widget.TextView;
 
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
 
-    int mRestoreStatus;
+    private static Theme sPreloaderTheme;
 
-    private TextView mDefaultView;
+    private final Rect mRect = new Rect();
+    private View mDefaultView;
     private OnClickListener mClickListener;
+    private final LauncherAppWidgetInfo mInfo;
+    private final int mStartState;
+    private final Intent mIconLookupIntent;
 
-    public PendingAppWidgetHostView(Context context, int restoreStatus) {
+    private Bitmap mIcon;
+    private PreloadIconDrawable mDrawable;
+
+    private Drawable mCenterDrawable;
+    private Drawable mTopCornerDrawable;
+
+    private boolean mDrawableSizeChanged;
+
+    public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) {
         super(context);
-        mRestoreStatus = restoreStatus;
+        mInfo = info;
+        mStartState = info.restoreStatus;
+        mIconLookupIntent = new Intent().setComponent(info.providerName);
+
+        setBackgroundResource(R.drawable.quantum_panel_dark);
+        setWillNotDraw(false);
     }
 
     @Override
@@ -27,7 +65,7 @@
     @Override
     protected View getDefaultView() {
         if (mDefaultView == null) {
-            mDefaultView = (TextView) mInflater.inflate(R.layout.appwidget_not_ready, this, false);
+            mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
             mDefaultView.setOnClickListener(this);
             applyState();
         }
@@ -39,26 +77,57 @@
         mClickListener = l;
     }
 
-    public void setStatus(int status) {
-        if (mRestoreStatus != status) {
-            mRestoreStatus = status;
-            applyState();
+    @Override
+    public boolean isReinflateRequired() {
+        // Re inflate is required any time the widget restore status changes
+        return mStartState != mInfo.restoreStatus;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mDrawableSizeChanged = true;
+    }
+
+    public void updateIcon(IconCache cache) {
+        Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
+        if (mIcon == icon) {
+            return;
+        }
+        mIcon = icon;
+        if (mDrawable != null) {
+            mDrawable.setCallback(null);
+            mDrawable = null;
+        }
+        if (mIcon != null) {
+            // The view displays two modes, one with a setup icon and another with a preload icon
+            // in the center.
+            if (isReadyForClickSetup()) {
+                mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
+                mTopCornerDrawable = new FastBitmapDrawable(mIcon);
+            } else {
+                if (sPreloaderTheme == null) {
+                    sPreloaderTheme = getResources().newTheme();
+                    sPreloaderTheme.applyStyle(R.style.PreloadIcon, true);
+                }
+
+                FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon);
+                mDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
+                mDrawable.setCallback(this);
+                applyState();
+            }
+            mDrawableSizeChanged = true;
         }
     }
 
     @Override
-    public boolean isReinflateRequired() {
-        // Re inflate is required if the the widget is restored.
-        return mRestoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED;
+    protected boolean verifyDrawable(Drawable who) {
+        return (who == mDrawable) || super.verifyDrawable(who);
     }
 
-    private void applyState() {
-        if (mDefaultView != null) {
-            if (isReadyForClickSetup()) {
-                mDefaultView.setText(R.string.gadget_setup_text);
-            } else {
-                mDefaultView.setText(R.string.gadget_pending_text);
-            }
+    public void applyState() {
+        if (mDrawable != null) {
+            mDrawable.setLevel(mInfo.installProgress);
         }
     }
 
@@ -72,7 +141,51 @@
     }
 
     public boolean isReadyForClickSetup() {
-        return (mRestoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
-                && (mRestoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
+        return (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
+                && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
     }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mDrawable != null) {
+            if (mDrawableSizeChanged) {
+                int maxSize = LauncherAppState.getInstance().getDynamicGrid()
+                        .getDeviceProfile().iconSizePx + 2 * mDrawable.getOutset();
+                int size = Math.min(maxSize, Math.min(
+                        getWidth() - getPaddingLeft() - getPaddingRight(),
+                        getHeight() - getPaddingTop() - getPaddingBottom()));
+
+                mRect.set(0, 0, size, size);
+                mRect.inset(mDrawable.getOutset(), mDrawable.getOutset());
+                mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+                mDrawable.setBounds(mRect);
+                mDrawableSizeChanged = false;
+            }
+
+            mDrawable.draw(canvas);
+        } else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) {
+            if (mDrawableSizeChanged) {
+                int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+                int paddingTop = getPaddingTop();
+                int paddingLeft = getPaddingLeft();
+
+                int size = Math.min(iconSize, Math.min(
+                        getWidth() - paddingLeft - getPaddingRight(),
+                        getHeight() - paddingTop - getPaddingBottom()));
+                mRect.set(0, 0, size, size);
+                mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+                mCenterDrawable.setBounds(mRect);
+
+                size = Math.min(size / 2,
+                        Math.max(mRect.top - paddingTop, mRect.left - paddingLeft));
+                mTopCornerDrawable.setBounds(paddingLeft, paddingTop,
+                        paddingLeft + size, paddingTop + size);
+                mDrawableSizeChanged = false;
+            }
+
+            mCenterDrawable.draw(canvas);
+            mTopCornerDrawable.draw(canvas);
+        }
+    }
+
 }
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index d9365cc..2972c4f 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -1,96 +1,143 @@
 package com.android.launcher3;
 
 import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
 class PreloadIconDrawable extends Drawable {
+
     private static final float ANIMATION_PROGRESS_STOPPED = -1.0f;
     private static final float ANIMATION_PROGRESS_STARTED = 0f;
     private static final float ANIMATION_PROGRESS_COMPLETED = 1.0f;
 
-    private static final float ICON_SCALE_FACTOR = 0.6f;
+    private static final float MIN_SATUNATION = 0.2f;
+    private static final float MIN_LIGHTNESS = 0.6f;
 
-    private static Bitmap sProgressBg, sProgressFill;
+    private static final float ICON_SCALE_FACTOR = 0.5f;
+    private static final int DEFAULT_COLOR = 0xFF009688;
 
-    private final Rect mCanvasClipRect = new Rect();
-    private final RectF mRect = new RectF();
-    private final Path mProgressPath = new Path();
-    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    private static final Rect sTempRect = new Rect();
 
+    private final RectF mIndicatorRect = new RectF();
+    private boolean mIndicatorRectDirty;
+
+    private final Paint mPaint;
     final Drawable mIcon;
 
+    private Drawable mBgDrawable;
+    private int mRingOutset;
+
+    private int mIndicatorColor = 0;
+
     /**
      * Indicates the progress of the preloader [0-100]. If it goes above 100, only the icon
      * is shown with no progress bar.
      */
     private int mProgress = 0;
-    private boolean mPathChanged;
 
     private float mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
     private ObjectAnimator mAnimator;
 
-    public PreloadIconDrawable(Drawable icon, Resources res) {
+    public PreloadIconDrawable(Drawable icon, Theme theme) {
         mIcon = icon;
 
-        setBounds(icon.getBounds());
-        mPathChanged = false;
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeCap(Paint.Cap.ROUND);
 
-        if (sProgressBg == null) {
-            sProgressBg = BitmapFactory.decodeResource(res, R.drawable.bg_preloader);
+        setBounds(icon.getBounds());
+        applyTheme(theme);
+        onLevelChange(0);
+    }
+
+    @Override
+    public void applyTheme(Theme t) {
+        TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable);
+        mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background);
+        mBgDrawable.setFilterBitmap(true);
+        mPaint.setStrokeWidth(ta.getDimension(R.styleable.PreloadIconDrawable_indicatorSize, 0));
+        mRingOutset = ta.getDimensionPixelSize(R.styleable.PreloadIconDrawable_ringOutset, 0);
+        ta.recycle();
+        onBoundsChange(getBounds());
+        invalidateSelf();
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mIcon.setBounds(bounds);
+        if (mBgDrawable != null) {
+            sTempRect.set(bounds);
+            sTempRect.inset(-mRingOutset, -mRingOutset);
+            mBgDrawable.setBounds(sTempRect);
         }
-        if (sProgressFill == null) {
-            sProgressFill = BitmapFactory.decodeResource(res, R.drawable.bg_preloader_progress);
-        }
+        mIndicatorRectDirty = true;
+    }
+
+    public int getOutset() {
+        return mRingOutset;
+    }
+
+    /**
+     * The size of the indicator is same as the content region of the {@link #mBgDrawable} minus
+     * half the stroke size to accommodate the indicator.
+     */
+    private void initIndicatorRect() {
+        Drawable d = mBgDrawable;
+        Rect bounds = d.getBounds();
+
+        d.getPadding(sTempRect);
+        // Amount by which padding has to be scaled
+        float paddingScaleX = ((float) bounds.width()) / d.getIntrinsicWidth();
+        float paddingScaleY = ((float) bounds.height()) / d.getIntrinsicHeight();
+        mIndicatorRect.set(
+                bounds.left + sTempRect.left * paddingScaleX,
+                bounds.top + sTempRect.top * paddingScaleY,
+                bounds.right - sTempRect.right * paddingScaleX,
+                bounds.bottom - sTempRect.bottom * paddingScaleY);
+
+        float inset = mPaint.getStrokeWidth() / 2;
+        mIndicatorRect.inset(inset, inset);
+        mIndicatorRectDirty = false;
     }
 
     @Override
     public void draw(Canvas canvas) {
-        final Rect r = getBounds();
-        if (canvas.getClipBounds(mCanvasClipRect) && !Rect.intersects(mCanvasClipRect, r)) {
+        final Rect r = new Rect(getBounds());
+        if (canvas.getClipBounds(sTempRect) && !Rect.intersects(sTempRect, r)) {
             // The draw region has been clipped.
             return;
         }
+        if (mIndicatorRectDirty) {
+            initIndicatorRect();
+        }
         final float iconScale;
 
         if ((mAnimationProgress >= ANIMATION_PROGRESS_STARTED)
                 && (mAnimationProgress < ANIMATION_PROGRESS_COMPLETED)) {
             mPaint.setAlpha((int) ((1 - mAnimationProgress) * 255));
-            canvas.drawBitmap(sProgressBg, null, r, mPaint);
-            canvas.drawBitmap(sProgressFill, null, r, mPaint);
-            iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress;
+            mBgDrawable.setAlpha(mPaint.getAlpha());
+            mBgDrawable.draw(canvas);
+            canvas.drawOval(mIndicatorRect, mPaint);
 
+            iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress;
         } else if (mAnimationProgress == ANIMATION_PROGRESS_STOPPED) {
             mPaint.setAlpha(255);
             iconScale = ICON_SCALE_FACTOR;
-            canvas.drawBitmap(sProgressBg, null, r, mPaint);
+            mBgDrawable.setAlpha(255);
+            mBgDrawable.draw(canvas);
 
             if (mProgress >= 100) {
-                canvas.drawBitmap(sProgressFill, null, r, mPaint);
+                canvas.drawOval(mIndicatorRect, mPaint);
             } else if (mProgress > 0) {
-                if (mPathChanged) {
-                    mProgressPath.reset();
-                    mProgressPath.moveTo(r.exactCenterX(), r.centerY());
-
-                    mRect.set(r);
-                    mProgressPath.arcTo(mRect, -90, mProgress * 3.6f);
-                    mProgressPath.close();
-                    mPathChanged = false;
-                }
-
-                canvas.save();
-                canvas.clipPath(mProgressPath);
-                canvas.drawBitmap(sProgressFill, null, r, mPaint);
-                canvas.restore();
+                canvas.drawArc(mIndicatorRect, -90, mProgress * 3.6f, false, mPaint);
             }
         } else {
             iconScale = 1;
@@ -103,12 +150,6 @@
     }
 
     @Override
-    protected void onBoundsChange(Rect bounds) {
-        mIcon.setBounds(bounds);
-        mPathChanged = true;
-    }
-
-    @Override
     public int getOpacity() {
         return PixelFormat.TRANSLUCENT;
     }
@@ -126,7 +167,6 @@
     @Override
     protected boolean onLevelChange(int level) {
         mProgress = level;
-        mPathChanged = true;
 
         // Stop Animation
         if (mAnimator != null) {
@@ -134,6 +174,14 @@
             mAnimator = null;
         }
         mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+        if (level > 0) {
+            // Set the paint color only when the level changes, so that the dominant color
+            // is only calculated when needed.
+            mPaint.setColor(getIndicatorColor());
+        }
+        if (mIcon instanceof FastBitmapDrawable) {
+            ((FastBitmapDrawable) mIcon).setGhostModeEnabled(level <= 0);
+        }
 
         invalidateSelf();
         return true;
@@ -165,4 +213,37 @@
     public float getAnimationProgress() {
         return mAnimationProgress;
     }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mIcon.getIntrinsicHeight();
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mIcon.getIntrinsicWidth();
+    }
+
+    private int getIndicatorColor() {
+        if (mIndicatorColor != 0) {
+            return mIndicatorColor;
+        }
+        if (!(mIcon instanceof FastBitmapDrawable)) {
+            mIndicatorColor = DEFAULT_COLOR;
+            return mIndicatorColor;
+        }
+        mIndicatorColor = Utilities.findDominantColorByHue(
+                ((FastBitmapDrawable) mIcon).getBitmap(), 20);
+
+        // Make sure that the dominant color has enough saturation to be visible properly.
+        float[] hsv = new float[3];
+        Color.colorToHSV(mIndicatorColor, hsv);
+        if (hsv[1] < MIN_SATUNATION) {
+            mIndicatorColor = DEFAULT_COLOR;
+            return mIndicatorColor;
+        }
+        hsv[2] = Math.max(MIN_LIGHTNESS, hsv[2]);
+        mIndicatorColor = Color.HSVToColor(hsv);
+        return mIndicatorColor;
+    }
 }
diff --git a/src/com/android/launcher3/PreloadReceiver.java b/src/com/android/launcher3/PreloadReceiver.java
deleted file mode 100644
index ca25746..0000000
--- a/src/com/android/launcher3/PreloadReceiver.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.text.TextUtils;
-import android.util.Log;
-
-public class PreloadReceiver extends BroadcastReceiver {
-    private static final String TAG = "Launcher.PreloadReceiver";
-    private static final boolean LOGD = false;
-
-    public static final String EXTRA_WORKSPACE_NAME =
-            "com.android.launcher3.action.EXTRA_WORKSPACE_NAME";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        final LauncherProvider provider = LauncherAppState.getLauncherProvider();
-        if (provider != null) {
-            String name = intent.getStringExtra(EXTRA_WORKSPACE_NAME);
-            final int workspaceResId = !TextUtils.isEmpty(name)
-                    ? context.getResources().getIdentifier(name, "xml", "com.android.launcher3") : 0;
-            if (LOGD) {
-                Log.d(TAG, "workspace name: " + name + " id: " + workspaceResId);
-            }
-            new AsyncTask<Void, Void, Void>() {
-                public Void doInBackground(Void ... args) {
-                    provider.loadDefaultFavoritesIfNecessary(workspaceResId);
-                    return null;
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0a711c5..f70fc4d 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -37,8 +38,9 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
-import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
 import android.view.View;
 import android.widget.Toast;
 
@@ -65,6 +67,8 @@
     static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
     static int sColorIndex = 0;
 
+    static int[] sLoc0 = new int[2];
+    static int[] sLoc1 = new int[2];
 
     // To turn on these properties, type
     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
@@ -326,9 +330,6 @@
 
     private static void initStatics(Context context) {
         final Resources resources = context.getResources();
-        final DisplayMetrics metrics = resources.getDisplayMetrics();
-        final float density = metrics.density;
-
         sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size);
         sIconTextureWidth = sIconTextureHeight = sIconWidth;
     }
@@ -347,6 +348,25 @@
         }
     }
 
+    public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) {
+        v0.getLocationInWindow(sLoc0);
+        v1.getLocationInWindow(sLoc1);
+
+        sLoc0[0] += (v0.getMeasuredWidth() * v0.getScaleX()) / 2;
+        sLoc0[1] += (v0.getMeasuredHeight() * v0.getScaleY()) / 2;
+        sLoc1[0] += (v1.getMeasuredWidth() * v1.getScaleX()) / 2;
+        sLoc1[1] += (v1.getMeasuredHeight() * v1.getScaleY()) / 2;
+
+        if (delta == null) {
+            delta = new int[2];
+        }
+
+        delta[0] = sLoc1[0] - sLoc0[0];
+        delta[1] = sLoc1[1] - sLoc0[1];
+
+        return delta;
+    }
+
     public static void scaleRectAboutCenter(Rect r, float scale) {
         int cx = r.centerX();
         int cy = r.centerY();
@@ -393,4 +413,105 @@
             return false;
         }
     }
+
+    /**
+     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+     * @param bitmap The bitmap to scan
+     * @param samples The approximate max number of samples to use.
+     */
+    static int findDominantColorByHue(Bitmap bitmap, int samples) {
+        final int height = bitmap.getHeight();
+        final int width = bitmap.getWidth();
+        int sampleStride = (int) Math.sqrt((height * width) / samples);
+        if (sampleStride < 1) {
+            sampleStride = 1;
+        }
+
+        // This is an out-param, for getting the hsv values for an rgb
+        float[] hsv = new float[3];
+
+        // First get the best hue, by creating a histogram over 360 hue buckets,
+        // where each pixel contributes a score weighted by saturation, value, and alpha.
+        float[] hueScoreHistogram = new float[360];
+        float highScore = -1;
+        int bestHue = -1;
+
+        for (int y = 0; y < height; y += sampleStride) {
+            for (int x = 0; x < width; x += sampleStride) {
+                int argb = bitmap.getPixel(x, y);
+                int alpha = 0xFF & (argb >> 24);
+                if (alpha < 0x80) {
+                    // Drop mostly-transparent pixels.
+                    continue;
+                }
+                // Remove the alpha channel.
+                int rgb = argb | 0xFF000000;
+                Color.colorToHSV(rgb, hsv);
+                // Bucket colors by the 360 integer hues.
+                int hue = (int) hsv[0];
+                if (hue < 0 || hue >= hueScoreHistogram.length) {
+                    // Defensively avoid array bounds violations.
+                    continue;
+                }
+                float score = hsv[1] * hsv[2];
+                hueScoreHistogram[hue] += score;
+                if (hueScoreHistogram[hue] > highScore) {
+                    highScore = hueScoreHistogram[hue];
+                    bestHue = hue;
+                }
+            }
+        }
+
+        SparseArray<Float> rgbScores = new SparseArray<Float>();
+        int bestColor = 0xff000000;
+        highScore = -1;
+        // Go back over the RGB colors that match the winning hue,
+        // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+        // The highest-scoring RGB color wins.
+        for (int y = 0; y < height; y += sampleStride) {
+            for (int x = 0; x < width; x += sampleStride) {
+                int rgb = bitmap.getPixel(x, y) | 0xff000000;
+                Color.colorToHSV(rgb, hsv);
+                int hue = (int) hsv[0];
+                if (hue == bestHue) {
+                    float s = hsv[1];
+                    float v = hsv[2];
+                    int bucket = (int) (s * 100) + (int) (v * 10000);
+                    // Score by cumulative saturation * value.
+                    float score = s * v;
+                    Float oldTotal = rgbScores.get(bucket);
+                    float newTotal = oldTotal == null ? score : oldTotal + score;
+                    rgbScores.put(bucket, newTotal);
+                    if (newTotal > highScore) {
+                        highScore = newTotal;
+                        // All the colors in the winning bucket are very similar. Last in wins.
+                        bestColor = rgb;
+                    }
+                }
+            }
+        }
+        return bestColor;
+    }
+
+    /*
+     * Finds a system apk which had a broadcast receiver listening to a particular action.
+     * @param action intent action used to find the apk
+     * @return a pair of apk package name and the resources.
+     */
+    static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
+        final Intent intent = new Intent(action);
+        for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
+            if (info.activityInfo != null &&
+                    (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                final String packageName = info.activityInfo.packageName;
+                try {
+                    final Resources res = pm.getResourcesForApplication(packageName);
+                    return Pair.create(packageName, res);
+                } catch (NameNotFoundException e) {
+                    Log.w(TAG, "Failed to find resources for " + packageName);
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index c8f2f33..733923d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -27,6 +27,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.WallpaperManager;
+import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -45,7 +46,10 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Handler.Callback;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Parcelable;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
@@ -73,6 +77,8 @@
 import java.util.Iterator;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -430,7 +436,6 @@
      * Initializes various states for this workspace.
      */
     protected void initWorkspace() {
-        Context context = getContext();
         mCurrentPage = mDefaultPage;
         Launcher.setScreen(mCurrentPage);
         LauncherAppState app = LauncherAppState.getInstance();
@@ -1986,6 +1991,12 @@
         d.copyBounds(bounds);
         if (bounds.width() == 0 || bounds.height() == 0) {
             bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+        } else {
+            bounds.offsetTo(0, 0);
+        }
+        if (d instanceof PreloadIconDrawable) {
+            int inset = -((PreloadIconDrawable) d).getOutset();
+            bounds.inset(inset, inset);
         }
         return bounds;
     }
@@ -2013,7 +2024,7 @@
         Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
                 Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(b);
-        drawDragView(v, c, 0, true);
+        drawDragView(v, c, 0);
         c.setBitmap(null);
 
         // The outline is used to visualize where the item will land if dropped
@@ -2230,9 +2241,6 @@
             } else if (stateIsOverview || stateIsOverviewHidden) {
                 mNewScale = mOverviewModeShrinkFactor;
             }
-            if (workspaceToAllApps) {
-                updateChildrenLayersEnabled(false);
-            }
         }
 
         final int duration;
@@ -2528,18 +2536,18 @@
      * @param destCanvas the canvas to draw on
      * @param padding the horizontal and vertical padding to use when drawing
      */
-    private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
+    private void drawDragView(View v, Canvas destCanvas, int padding) {
         final Rect clipRect = mTempRect;
         v.getDrawingRect(clipRect);
 
         boolean textVisible = false;
 
         destCanvas.save();
-        if (v instanceof TextView && pruneToDrawable) {
+        if (v instanceof TextView) {
             Drawable d = ((TextView) v).getCompoundDrawables()[1];
             Rect bounds = getDrawableBounds(d);
             clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
-            destCanvas.translate(padding / 2, padding / 2);
+            destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
             d.draw(destCanvas);
         } else {
             if (v instanceof FolderIcon) {
@@ -2549,14 +2557,6 @@
                     ((FolderIcon) v).setTextVisible(false);
                     textVisible = true;
                 }
-            } else if (v instanceof BubbleTextView) {
-                final BubbleTextView tv = (BubbleTextView) v;
-                clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
-                        tv.getLayout().getLineTop(0);
-            } else if (v instanceof TextView) {
-                final TextView tv = (TextView) v;
-                clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
-                        tv.getLayout().getLineTop(0);
             }
             destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
             destCanvas.clipRect(clipRect, Op.REPLACE);
@@ -2573,22 +2573,26 @@
     /**
      * Returns a new bitmap to show when the given View is being dragged around.
      * Responsibility for the bitmap is transferred to the caller.
+     * @param expectedPadding padding to add to the drag view. If a different padding was used
+     * its value will be changed
      */
-    public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
+    public Bitmap createDragBitmap(View v, Canvas canvas, AtomicInteger expectedPadding) {
         Bitmap b;
 
+        int padding = expectedPadding.get();
         if (v instanceof TextView) {
             Drawable d = ((TextView) v).getCompoundDrawables()[1];
             Rect bounds = getDrawableBounds(d);
             b = Bitmap.createBitmap(bounds.width() + padding,
                     bounds.height() + padding, Bitmap.Config.ARGB_8888);
+            expectedPadding.set(padding - bounds.left - bounds.top);
         } else {
             b = Bitmap.createBitmap(
                     v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
         }
 
         canvas.setBitmap(b);
-        drawDragView(v, canvas, padding, true);
+        drawDragView(v, canvas, padding);
         canvas.setBitmap(null);
 
         return b;
@@ -2604,7 +2608,7 @@
                 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
 
         canvas.setBitmap(b);
-        drawDragView(v, canvas, padding, true);
+        drawDragView(v, canvas, padding);
         mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
         canvas.setBitmap(null);
         return b;
@@ -2664,7 +2668,8 @@
     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);
+        AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
+        final Bitmap b = createDragBitmap(child, new Canvas(), padding);
 
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
@@ -2672,7 +2677,7 @@
         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
-                        - DRAG_BITMAP_PADDING / 2);
+                        - padding.get() / 2);
 
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -2687,7 +2692,7 @@
             dragLayerY += top;
             // 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.
-            dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
+            dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
             dragRect = new Rect(left, top, right, bottom);
         } else if (child instanceof FolderIcon) {
             int previewSize = grid.folderIconSizePx;
@@ -2731,7 +2736,8 @@
         mLauncher.onDragStarted(child);
 
         // Compose a new drag bitmap that is of the icon size
-        final Bitmap tmpB = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
+        AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
+        final Bitmap tmpB = createDragBitmap(child, new Canvas(), padding);
         Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
         Paint p = new Paint();
         p.setFilterBitmap(true);
@@ -2749,7 +2755,7 @@
 
         // 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);
+        Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
         Rect dragRect = new Rect(0, 0, iconSize, iconSize);
 
         if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
@@ -3981,15 +3987,16 @@
             } else {
                 cellLayout.findCellForSpan(mTargetCell, 1, 1);
             }
+            // Add the item to DB before adding to screen ensures that the container and other
+            // values of the info is properly updated.
+            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
+                    mTargetCell[0], mTargetCell[1]);
+
             addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
                     info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
-            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
             cellLayout.getShortcutsAndWidgets().measureChild(view);
 
-            LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
-                    lp.cellX, lp.cellY);
-
             if (d.dragView != null) {
                 // We wrap the animation call in the temporary set and reset of the current
                 // cellLayout to its final transform -- this means we animate the drag view to
@@ -4869,7 +4876,7 @@
     }
 
     public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
-        HashSet<String> completedPackages = new HashSet<>();
+        HashSet<String> completedPackages = new HashSet<String>();
 
         for (final PackageInstallInfo installInfo : installInfos) {
             mapOverItems(MAP_RECURSE, new ItemOperator() {
@@ -4881,7 +4888,14 @@
                         ((ShortcutInfo) info).setProgress(installInfo.progress);
                         ((ShortcutInfo) info).setState(installInfo.state);
                         ((BubbleTextView)v).applyState();
+                    } else if (v instanceof PendingAppWidgetHostView
+                            && info instanceof LauncherAppWidgetInfo
+                            && ((LauncherAppWidgetInfo) info).providerName.getPackageName()
+                                .equals(installInfo.packageName)) {
+                        ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress;
+                        ((PendingAppWidgetHostView) v).applyState();
                     }
+
                     // process all the shortcuts
                     return false;
                 }
@@ -4898,7 +4912,8 @@
     }
 
     private void restorePendingWidgets(final Set<String> installedPackaged) {
-        final AtomicBoolean widgetsChanged = new AtomicBoolean(false);
+        final ArrayList<LauncherAppWidgetInfo> changedInfo = new ArrayList<LauncherAppWidgetInfo>();
+
         // Iterate non recursively as widgets can't be inside a folder.
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
 
@@ -4908,18 +4923,28 @@
                     LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
                     if (widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
                             && installedPackaged.contains(widgetInfo.providerName.getPackageName())) {
-                        widgetsChanged.set(true);
+
+                        changedInfo.add(widgetInfo);
+
+                        // Remove the provider not ready flag
+                        widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+                        LauncherModel.updateItemInDatabase(getContext(), widgetInfo);
                     }
                 }
                 // process all the widget
                 return false;
             }
         });
-        if (widgetsChanged.get()) {
-            // Reload layout and update widget status
-            // TODO instead of full reload, just update the specific widgets
-            getContext().getContentResolver()
-                .notifyChange(LauncherSettings.Favorites.CONTENT_URI, null);
+        if (!changedInfo.isEmpty()) {
+            DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
+                    mLauncher.getAppWidgetHost());
+            if (LauncherModel.findAppWidgetProviderInfoWithComponent(getContext(),
+                    changedInfo.get(0).providerName) != null) {
+                // Re-inflate the widgets which have changed status
+                widgetRefresh.run();
+            } else {
+                // widgetRefresh will automatically run when the packages are updated.
+            }
         }
     }
 
@@ -4997,4 +5022,53 @@
     public void getLocationInDragLayer(int[] loc) {
         mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
     }
+
+    /**
+     * Used as a workaround to ensure that the AppWidgetService receives the
+     * PACKAGE_ADDED broadcast before updating widgets.
+     */
+    private class DeferredWidgetRefresh implements Runnable {
+        private final ArrayList<LauncherAppWidgetInfo> mInfos;
+        private final LauncherAppWidgetHost mHost;
+        private final Handler mHandler;
+
+        private boolean mRefreshPending;
+
+        public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
+                LauncherAppWidgetHost host) {
+            mInfos = infos;
+            mHost = host;
+            mHandler = new Handler();
+            mRefreshPending = true;
+
+            mHost.addProviderChangeListener(this);
+            // Force refresh after 10 seconds, if we don't get the provider changed event.
+            // This could happen when the provider is no longer available in the app.
+            mHandler.postDelayed(this, 10000);
+        }
+
+        @Override
+        public void run() {
+            mHost.removeProviderChangeListener(this);
+            mHandler.removeCallbacks(this);
+
+            if (!mRefreshPending) {
+                return;
+            }
+
+            mRefreshPending = false;
+
+            for (LauncherAppWidgetInfo info : mInfos) {
+                if (info.hostView instanceof PendingAppWidgetHostView) {
+                    PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
+                    mLauncher.removeAppWidget(info);
+
+                    CellLayout cl = (CellLayout) view.getParent().getParent();
+                    // Remove the current widget
+                    cl.removeView(view);
+                    mLauncher.bindAppWidget(info);
+                }
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
index 653a88c..6a2a02e 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
@@ -86,7 +86,7 @@
             return;
         }
         mReplayPending = false;
-        ArrayList<PackageInstallInfo> updates = new ArrayList<>();
+        ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
         for (String packageName: mPrefs.getAll().keySet()) {
             final String json = mPrefs.getString(packageName, null);
             if (!TextUtils.isEmpty(json)) {
@@ -125,7 +125,7 @@
                 replayUpdates();
             } else {
                 LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-                ArrayList<PackageInstallInfo> update = new ArrayList<>();
+                ArrayList<PackageInstallInfo> update = new ArrayList<PackageInstallInfo>();
                 update.add(installInfo);
                 sendUpdate(app, update);
             }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 7f6302f..26e1b7c 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -33,7 +33,7 @@
     private static final String TAG = "PackageInstallerCompatVL";
     private static final boolean DEBUG = false;
 
-    private final SparseArray<InstallSessionInfo> mPendingReplays = new SparseArray<>();
+    private final SparseArray<InstallSessionInfo> mPendingReplays = new SparseArray<InstallSessionInfo>();
     private final PackageInstaller mInstaller;
 
     private boolean mResumed;
@@ -97,7 +97,7 @@
             return;
         }
 
-        ArrayList<PackageInstallInfo> updates = new ArrayList<>();
+        ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
         if (newInfo != null) {
             updates.add(newInfo);
         }