Defend against overlapping items in the workspace.

Should the Launcher's database become corrupted by
mysterious forces (e.g.: third-party launchers; botched
upgrades; smoke monsters) in such a way as to cause two
items to share the same cell, we now ignore loading the
latter.

Prevents a runtime crash (http://b/2655516).

Bug: 2655516
Change-Id: Ia514746f04f0e51b2cd07e9290589a6eab75bdd2
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index ca2ea86..a96d9ae 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -137,4 +137,9 @@
     
     void unbind() {
     }
+
+    @Override
+    public String toString() {
+        return "Item(id=" + this.id + " type=" + this.itemType + ")";
+    }
 }
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index a28973b..8499ebb 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -49,7 +49,7 @@
 
     @Override
     public String toString() {
-        return Integer.toString(appWidgetId);
+        return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
     }
 
 
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 6127649..26671ad 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -670,6 +670,28 @@
                 }
             }
 
+            // check & update map of what's occupied; used to discard overlapping/invalid items
+            private boolean checkItemPlacement(ItemInfo occupied[][][], ItemInfo item) {
+                for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
+                    for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
+                        if (occupied[item.screen][x][y] != null) {
+                            Log.e(TAG, "Error loading shortcut " + item
+                                + " into cell (" + item.screen + ":" 
+                                + x + "," + y
+                                + ") occupied by " 
+                                + occupied[item.screen][x][y]);
+                            return false;
+                        }
+                    }
+                }
+                for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
+                    for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
+                        occupied[item.screen][x][y] = item;
+                    }
+                }
+                return true;
+            }
+
             private void loadWorkspace() {
                 long t = SystemClock.uptimeMillis();
 
@@ -688,6 +710,8 @@
                 final Cursor c = contentResolver.query(
                         LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
 
+                final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
+
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
                     final int intentIndex = c.getColumnIndexOrThrow
@@ -762,6 +786,11 @@
                                     info.cellX = c.getInt(cellXIndex);
                                     info.cellY = c.getInt(cellYIndex);
 
+                                    // check & update map of what's occupied
+                                    if (!checkItemPlacement(occupied, info)) {
+                                        break;
+                                    }
+
                                     switch (container) {
                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                         mItems.add(info);
@@ -798,6 +827,11 @@
                                 folderInfo.cellX = c.getInt(cellXIndex);
                                 folderInfo.cellY = c.getInt(cellYIndex);
 
+                                // check & update map of what's occupied
+                                if (!checkItemPlacement(occupied, folderInfo)) {
+                                    break;
+                                }
+
                                 switch (container) {
                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
                                         mItems.add(folderInfo);
@@ -841,7 +875,12 @@
                                     liveFolderInfo.cellY = c.getInt(cellYIndex);
                                     liveFolderInfo.baseIntent = intent;
                                     liveFolderInfo.displayMode = c.getInt(displayModeIndex);
-    
+
+                                    // check & update map of what's occupied
+                                    if (!checkItemPlacement(occupied, liveFolderInfo)) {
+                                        break;
+                                    }
+
                                     loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
                                             iconResourceIndex, liveFolderInfo);
     
@@ -884,6 +923,11 @@
                                     }
                                     appWidgetInfo.container = c.getInt(containerIndex);
     
+                                    // check & update map of what's occupied
+                                    if (!checkItemPlacement(occupied, appWidgetInfo)) {
+                                        break;
+                                    }
+
                                     mAppWidgets.add(appWidgetInfo);
                                 }
                                 break;
@@ -916,6 +960,19 @@
 
                 if (DEBUG_LOADERS) {
                     Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
+                    Log.d(TAG, "workspace layout: ");
+                    for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
+                        String line = "";
+                        for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
+                            if (s > 0) {
+                                line += " | ";
+                            }
+                            for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
+                                line += ((occupied[s][x][y] != null) ? "#" : ".");
+                            }
+                        }
+                        Log.d(TAG, "[ " + line + " ]");
+                    }
                 }
             }