Fixing bug 5011917 - clearing refernce to old folders from FolderInfo

-> Also, ensured that unbind() gets called on all ItemInfos on rotate

Change-Id: I869b68fcae5c66702ec204596f5ecabdc7a32df7
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 788d4b9..78df80f 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -54,14 +54,12 @@
 public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener {
 
-    protected DragController mDragController;
-
-    protected Launcher mLauncher;
-
-    protected FolderInfo mInfo;
-
     private static final String TAG = "Launcher.Folder";
 
+    protected DragController mDragController;
+    protected Launcher mLauncher;
+    protected FolderInfo mInfo;
+
     static final int STATE_NONE = -1;
     static final int STATE_SMALL = 0;
     static final int STATE_ANIMATING = 1;
@@ -132,6 +130,8 @@
         if (sHintText == null) {
             sHintText = res.getString(R.string.folder_hint_text);
         }
+
+        mLauncher = (Launcher) context;
     }
 
     @Override
@@ -286,10 +286,6 @@
         mDragController = dragController;
     }
 
-    void setLauncher(Launcher launcher) {
-        mLauncher = launcher;
-    }
-
     void setFolderIcon(FolderIcon icon) {
         mFolderIcon = icon;
     }
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 283c295..952916d 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -127,7 +127,6 @@
 
         Folder folder = Folder.fromXml(launcher);
         folder.setDragController(launcher.getDragController());
-        folder.setLauncher(launcher);
         folder.setFolderIcon(icon);
         folder.bind(folderInfo);
         icon.mFolder = folder;
diff --git a/src/com/android/launcher2/FolderInfo.java b/src/com/android/launcher2/FolderInfo.java
index b5b5b29..3ae31d2 100644
--- a/src/com/android/launcher2/FolderInfo.java
+++ b/src/com/android/launcher2/FolderInfo.java
@@ -99,6 +99,12 @@
         }
     }
 
+    @Override
+    void unbind() {
+        super.unbind();
+        listeners.clear();
+    }
+
     interface FolderListener {
         public void onAdd(ShortcutInfo item);
         public void onRemove(ShortcutInfo item);
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index 3a1c29a..8d46624 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -144,7 +144,13 @@
             values.put(LauncherSettings.Favorites.ICON, data);
         }
     }
-    
+
+    /**
+     * It is very important that sub-classes implement this if they contain any references
+     * to the activity (anything in the view hierarchy etc.). If not, leaks can result since
+     * ItemInfo objects persist across rotation and can hence leak by holding stale references
+     * to the old view hierarchy / activity.
+     */
     void unbind() {
     }
 
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 33a86a9..5e500ba 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -221,8 +221,6 @@
 
     private static LocaleConfiguration sLocaleConfiguration = null;
 
-    private ArrayList<ItemInfo> mDesktopItems = new ArrayList<ItemInfo>();
-
     private static HashMap<Long, FolderInfo> sFolders = new HashMap<Long, FolderInfo>();
 
     // Hotseats (quick-launch icons next to AllApps)
@@ -1098,8 +1096,6 @@
                 screen, cellXY[0], cellXY[1], false);
 
         if (!mRestoring) {
-            mDesktopItems.add(launcherInfo);
-
             // Perform actual inflation because we're live
             launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
 
@@ -1228,7 +1224,6 @@
     }
 
     public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
-        mDesktopItems.remove(launcherInfo);
         removeWidgetToAutoAdvance(launcherInfo.hostView);
         launcherInfo.hostView = null;
     }
@@ -1359,7 +1354,7 @@
         TextKeyListener.getInstance().release();
 
 
-        unbindDesktopItems();
+        unbindWorkspaceItems();
 
         getContentResolver().unregisterContentObserver(mWidgetObserver);
         unregisterReceiver(mCloseSystemDialogsReceiver);
@@ -1743,11 +1738,8 @@
      * Go through the and disconnect any of the callbacks in the drawables and the views or we
      * leak the previous Home screen on orientation change.
      */
-    private void unbindDesktopItems() {
-        for (ItemInfo item: mDesktopItems) {
-            item.unbind();
-        }
-        mDesktopItems.clear();
+    private void unbindWorkspaceItems() {
+        LauncherModel.unbindWorkspaceItems();
     }
 
     /**
@@ -3016,9 +3008,8 @@
             });
         }
 
-        // This wasn't being called before which resulted in a leak of AppWidgetHostViews (through
-        // mDesktopItems -> AppWidgetInfo -> hostView).
-        unbindDesktopItems();
+        // This wasn't being called before which resulted in a leak of AppWidgetHostViews
+        unbindWorkspaceItems();
     }
 
     /**
@@ -3034,7 +3025,6 @@
 
         for (int i=start; i<end; i++) {
             final ItemInfo item = shortcuts.get(i);
-            mDesktopItems.add(item);
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@@ -3096,8 +3086,6 @@
 
         workspace.requestLayout();
 
-        mDesktopItems.add(item);
-
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
                     + (SystemClock.uptimeMillis()-start) + "ms");
@@ -3269,7 +3257,6 @@
         Log.d(TAG, "mRestoring=" + mRestoring);
         Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
         Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
-        Log.d(TAG, "mDesktopItems.size=" + mDesktopItems.size());
         Log.d(TAG, "sFolders.size=" + sFolders.size());
         mModel.dumpState();
 
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 64b38c0..ec629d0 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -98,7 +98,7 @@
     // sItems is passed to bindItems, which expects a list of all folders and shortcuts created by
     //       LauncherModel that are directly on the home screen (however, no widgets or shortcuts
     //       within folders).
-    static final ArrayList<ItemInfo> sItems = new ArrayList<ItemInfo>();
+    static final ArrayList<ItemInfo> sWorkspaceItems = new ArrayList<ItemInfo>();
 
     // sAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget()
     static final ArrayList<LauncherAppWidgetInfo> sAppWidgets =
@@ -148,6 +148,12 @@
         return Bitmap.createBitmap(mDefaultIcon);
     }
 
+    public static void unbindWorkspaceItems() {
+        for (ItemInfo item: sWorkspaceItems) {
+            item.unbind();
+        }
+    }
+
     /**
      * Adds an item to the DB if it was not created previously, or move it to a new
      * <container, screen, cellX, cellY>
@@ -197,11 +203,11 @@
                     // as in Workspace.onDrop. Here, we just add/remove them from the list of items
                     // that are on the desktop, as appropriate
                     if (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                        if (!sItems.contains(modelItem)) {
-                            sItems.add(modelItem);
+                        if (!sWorkspaceItems.contains(modelItem)) {
+                            sWorkspaceItems.add(modelItem);
                         }
                     } else {
-                        sItems.remove(modelItem);
+                        sWorkspaceItems.remove(modelItem);
                     }
                 }
             });
@@ -374,13 +380,13 @@
                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                         sFolders.put(item.id, (FolderInfo) item);
                         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                            sItems.add(item);
+                            sWorkspaceItems.add(item);
                         }
                         break;
                     case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                            sItems.add(item);
+                            sWorkspaceItems.add(item);
                         }
                         break;
                     case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -456,11 +462,11 @@
                     switch (item.itemType) {
                         case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                             sFolders.remove(item.id);
-                            sItems.remove(item);
+                            sWorkspaceItems.remove(item);
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                            sItems.remove(item);
+                            sWorkspaceItems.remove(item);
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                             sAppWidgets.remove((LauncherAppWidgetInfo) item);
@@ -482,7 +488,7 @@
                     cr.delete(LauncherSettings.Favorites.getContentUri(info.id, false), null, null);
                     sItemsIdMap.remove(info.id);
                     sFolders.remove(info.id);
-                    sItems.remove(info);
+                    sWorkspaceItems.remove(info);
 
                     cr.delete(LauncherSettings.Favorites.CONTENT_URI,
                             LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
@@ -819,7 +825,7 @@
             final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
             final boolean isSafeMode = manager.isSafeMode();
 
-            sItems.clear();
+            sWorkspaceItems.clear();
             sAppWidgets.clear();
             sFolders.clear();
             sItemsIdMap.clear();
@@ -911,7 +917,7 @@
 
                                 switch (container) {
                                 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                    sItems.add(info);
+                                    sWorkspaceItems.add(info);
                                     break;
                                 default:
                                     // Item is in a user folder
@@ -955,7 +961,7 @@
                             }
                             switch (container) {
                                 case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                    sItems.add(folderInfo);
+                                    sWorkspaceItems.add(folderInfo);
                                     break;
                             }
 
@@ -1072,7 +1078,7 @@
                 }
             });
             // Add the items to the workspace.
-            N = sItems.size();
+            N = sWorkspaceItems.size();
             for (int i=0; i<N; i+=ITEMS_CHUNK) {
                 final int start = i;
                 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
@@ -1080,7 +1086,7 @@
                     public void run() {
                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                         if (callbacks != null) {
-                            callbacks.bindItems(sItems, start, start+chunkSize);
+                            callbacks.bindItems(sWorkspaceItems, start, start+chunkSize);
                         }
                     }
                 });
@@ -1317,7 +1323,7 @@
             Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
             Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
             Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
-            Log.d(TAG, "mItems size=" + sItems.size());
+            Log.d(TAG, "mItems size=" + sWorkspaceItems.size());
         }
     }