Merge "Updating Tablet clings. (Bug 11973614)" into jb-ub-now-kermit
diff --git a/res/drawable-hdpi/screenpanel_hover.9.png b/res/drawable-hdpi/screenpanel_hover.9.png
index 3321fc9..0fed7c9 100644
--- a/res/drawable-hdpi/screenpanel_hover.9.png
+++ b/res/drawable-hdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel_hover.9.png b/res/drawable-mdpi/screenpanel_hover.9.png
index dd77406..7dd8858 100644
--- a/res/drawable-mdpi/screenpanel_hover.9.png
+++ b/res/drawable-mdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel_hover.9.png b/res/drawable-xhdpi/screenpanel_hover.9.png
index a44dc11..251bf20 100644
--- a/res/drawable-xhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel_hover.9.png b/res/drawable-xxhdpi/screenpanel_hover.9.png
index 1ab18da..e8b36d8 100644
--- a/res/drawable-xxhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xxhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 37cdb9e..49b12b1 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -299,6 +299,13 @@
         mPageLayoutPaddingBottom = pageIndicatorHeight;
     }
 
+    WidgetPreviewLoader getWidgetPreviewLoader() {
+        if (mWidgetPreviewLoader == null) {
+            mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher);
+        }
+        return mWidgetPreviewLoader;
+    }
+
     /** Returns the item index of the center item on this page so that we can restore to this
      *  item index when we rotate. */
     private int getMiddleComponentIndexOnCurrentPage() {
@@ -364,10 +371,6 @@
     }
 
     protected void onDataReady(int width, int height) {
-        if (mWidgetPreviewLoader == null) {
-            mWidgetPreviewLoader = new WidgetPreviewLoader(mLauncher);
-        }
-
         // Now that the data is ready, we can calculate the content width, the number of cells to
         // use for each page
         LauncherAppState app = LauncherAppState.getInstance();
@@ -727,13 +730,13 @@
 
             int[] previewSizeBeforeScale = new int[1];
 
-            preview = mWidgetPreviewLoader.generateWidgetPreview(createWidgetInfo.componentName,
+            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.componentName,
                     createWidgetInfo.previewImage, createWidgetInfo.icon, spanX, spanY,
                     maxWidth, maxHeight, null, previewSizeBeforeScale);
 
             // Compare the size of the drag preview to the preview in the AppsCustomize tray
             int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
-                    mWidgetPreviewLoader.maxWidthForWidgetPreview(spanX));
+                    getWidgetPreviewLoader().maxWidthForWidgetPreview(spanX));
             scale = previewWidthInAppsCustomize / (float) preview.getWidth();
 
             // The bitmap in the AppsCustomize tray is always the the same size, so there
@@ -1147,7 +1150,7 @@
                     // do cleanup inside onSyncWidgetPageItems
                     onSyncWidgetPageItems(data, false);
                 }
-            }, mWidgetPreviewLoader);
+            }, getWidgetPreviewLoader());
 
         // Ensure that the task is appropriately prioritized and runs in parallel
         AppsCustomizeAsyncTask t = new AppsCustomizeAsyncTask(page,
@@ -1208,7 +1211,7 @@
                 createItemInfo.minSpanX = minSpanXY[0];
                 createItemInfo.minSpanY = minSpanXY[1];
 
-                widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, mWidgetPreviewLoader);
+                widget.applyFromAppWidgetProviderInfo(info, -1, spanXY, getWidgetPreviewLoader());
                 widget.setTag(createItemInfo);
                 widget.setShortPressListener(this);
             } else if (rawInfo instanceof ResolveInfo) {
@@ -1218,7 +1221,7 @@
                 createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
                 createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
                         info.activityInfo.name);
-                widget.applyFromResolveInfo(mPackageManager, info, mWidgetPreviewLoader);
+                widget.applyFromResolveInfo(mPackageManager, info, getWidgetPreviewLoader());
                 widget.setTag(createItemInfo);
             }
             widget.setOnClickListener(this);
@@ -1255,11 +1258,11 @@
                     maxPreviewHeight = maxSize[1];
                 }
 
-                mWidgetPreviewLoader.setPreviewSize(
+                getWidgetPreviewLoader().setPreviewSize(
                         maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout);
                 if (immediate) {
                     AsyncTaskPageData data = new AsyncTaskPageData(page, items,
-                            maxPreviewWidth, maxPreviewHeight, null, null, mWidgetPreviewLoader);
+                            maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader());
                     loadWidgetPreviewsInBackground(null, data);
                     onSyncWidgetPageItems(data, immediate);
                 } else {
@@ -1296,7 +1299,7 @@
                 task.syncThreadPriority();
             }
 
-            images.add(mWidgetPreviewLoader.getPreview(items.get(i)));
+            images.add(getWidgetPreviewLoader().getPreview(items.get(i)));
         }
     }
 
@@ -1725,4 +1728,4 @@
 
         return String.format(getContext().getString(stringId), page + 1, count);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 3aced1f..cbde991 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -71,6 +71,8 @@
                 296, 491.33f,  4, 4,  48, 13, (hasAA ? 5 : 5), 48));
         deviceProfiles.add(new DeviceProfile("Nexus 4",
                 359, 518,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56));
+        deviceProfiles.add(new DeviceProfile("Large Phone",
+                400, 680,  5, 5,  64, 14.4f,  5, 56));
         // The tablet profile is odd in that the landscape orientation
         // also includes the nav bar on the side
         deviceProfiles.add(new DeviceProfile("Nexus 7",
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4478e9b..2102a1f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2709,7 +2709,7 @@
         return widgetsAndShortcuts;
     }
 
-    private boolean isPackageDisabled(PackageManager pm, String packageName) {
+    private static boolean isPackageDisabled(PackageManager pm, String packageName) {
         try {
             PackageInfo pi = pm.getPackageInfo(packageName, 0);
             return !pi.applicationInfo.enabled;
@@ -2718,7 +2718,8 @@
         }
         return false;
     }
-    private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
+
+    public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
         if (cn == null) {
             return false;
         }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5676a9a..c797cc4 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1474,7 +1474,6 @@
                 // Ignore
             }
 
-
             // We already have a favorites database in the old provider
             if (c != null) {
                 try {
@@ -1516,10 +1515,12 @@
                         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());
 
-                        final ContentValues[] rows = new ContentValues[c.getCount()];
+                        final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
+                        final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
 
                         while (c.moveToNext()) {
                             final int itemType = c.getInt(itemTypeIndex);
@@ -1538,29 +1539,43 @@
                                 + c.getString(titleIndex) + "\": " + intentStr, true);
 
                             if (itemType != Favorites.ITEM_TYPE_FOLDER) {
+
+                                final Intent intent;
+                                final ComponentName cn;
+                                try {
+                                    intent = Intent.parseUri(intentStr, 0);
+                                } catch (URISyntaxException e) {
+                                    // bogus intent?
+                                    Launcher.addDumpLog(TAG,
+                                            "skipping invalid intent uri", true);
+                                    continue;
+                                }
+
+                                cn = intent.getComponent();
                                 if (TextUtils.isEmpty(intentStr)) {
                                     // no intent? no icon
                                     Launcher.addDumpLog(TAG, "skipping empty intent", true);
                                     continue;
-                                } else {
-                                    try {
-                                        // Canonicalize
-                                        final Intent intent = Intent.parseUri(intentStr, 0);
-                                        // the Play Store sets the package parameter, but Launcher
-                                        // does not, so we clear that out to keep them the same
-                                        intent.setPackage(null);
-                                        final String key = intent.toUri(0);
-                                        if (seenIntents.contains(key)) {
-                                            Launcher.addDumpLog(TAG, "skipping duplicate", true);
-                                            continue;
-                                        } else {
-                                            seenIntents.add(key);
-                                        }
-                                    } catch (URISyntaxException e) {
-                                        // bogus intent?
-                                        Launcher.addDumpLog(TAG,
-                                                "skipping invalid intent uri", true);
+                                } else if (cn != null &&
+                                        !LauncherModel.isValidPackageComponent(pm, cn)) {
+                                    // component no longer exists.
+                                    Launcher.addDumpLog(TAG, "skipping item whose component " +
+                                            "no longer exists.", true);
+                                    continue;
+                                } else if (container ==
+                                        LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                    // Dedupe icons directly on the workspace
+
+                                    // Canonicalize
+                                    // the Play Store sets the package parameter, but Launcher
+                                    // does not, so we clear that out to keep them the same
+                                    intent.setPackage(null);
+                                    final String key = intent.toUri(0);
+                                    if (seenIntents.contains(key)) {
+                                        Launcher.addDumpLog(TAG, "skipping duplicate", true);
                                         continue;
+                                    } else {
+                                        seenIntents.add(key);
                                     }
                                 }
                             }
@@ -1583,7 +1598,8 @@
                                     c.getInt(displayModeIndex));
 
                             if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
-                                    && screen >= hotseatWidth) {
+                                    && (screen >= hotseatWidth ||
+                                        screen == grid.hotseatAllAppsRank)) {
                                 // no room for you in the hotseat? it's off to the desktop with you
                                 container = Favorites.CONTAINER_DESKTOP;
                             }
@@ -1594,32 +1610,53 @@
                                 values.put(LauncherSettings.Favorites.CELLX, cellX);
                                 values.put(LauncherSettings.Favorites.CELLY, cellY);
                             } else {
-                                values.put(LauncherSettings.Favorites.SCREEN, curScreen);
-                                values.put(LauncherSettings.Favorites.CELLX, curX);
-                                values.put(LauncherSettings.Favorites.CELLY, curY);
-                                curX = (curX + 1) % width;
-                                if (curX == 0) {
-                                    curY = (curY + 1);
-                                }
-                                // Leave the last row of icons blank on screen 0
-                                if (curScreen == 0 && curY == height - 1 || curY == height) {
-                                    curScreen = (int) generateNewScreenId();
-                                    curY = 0;
-                                }
+                                // For items contained directly on one of the workspace screen,
+                                // we'll determine their location (screen, x, y) in a second pass.
                             }
 
                             values.put(LauncherSettings.Favorites.CONTAINER, container);
 
-                            rows[i++] = values;
+                            if (itemType != Favorites.ITEM_TYPE_FOLDER) {
+                                shortcuts.add(values);
+                            } else {
+                                folders.add(values);
+                            }
                         }
 
-                        if (i > 0) {
+                        final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
+                        // Folders first
+                        allItems.addAll(folders);
+                        // Then shortcuts
+                        allItems.addAll(shortcuts);
+
+                        // Layout all the folders
+                        for (ContentValues values: allItems) {
+                            if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
+                                    LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                // Hotseat items and folder items have already had their
+                                // location information set. Nothing to be done here.
+                                continue;
+                            }
+                            values.put(LauncherSettings.Favorites.SCREEN, curScreen);
+                            values.put(LauncherSettings.Favorites.CELLX, curX);
+                            values.put(LauncherSettings.Favorites.CELLY, curY);
+                            curX = (curX + 1) % width;
+                            if (curX == 0) {
+                                curY = (curY + 1);
+                            }
+                            // Leave the last row of icons blank on every screen
+                            if (curY == height - 1) {
+                                curScreen = (int) generateNewScreenId();
+                                curY = 0;
+                            }
+                        }
+
+                        if (allItems.size() > 0) {
                             db.beginTransaction();
                             try {
-                                final int N = rows.length;
-                                for (i = 0; i < N; i++) {
-                                    if (rows[i] == null) continue;
-                                    if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i])
+                                for (ContentValues row: allItems) {
+                                    if (row == null) continue;
+                                    if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
                                             < 0) {
                                         return;
                                     } else {