Remove LiveFolders when corresponding package is uninstalled.
Bug #2298872
diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java
index 561b345..00e9ea8 100644
--- a/src/com/android/launcher2/AllAppsList.java
+++ b/src/com/android/launcher2/AllAppsList.java
@@ -17,25 +17,13 @@
 package com.android.launcher2;
 
 import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Intent;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.util.Log;
-import android.os.Process;
 
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 
@@ -46,13 +34,15 @@
     public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
     
     /** The list off all apps. */
-    public ArrayList<ApplicationInfo> data = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
+    public ArrayList<ApplicationInfo> data =
+            new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
     /** The list of apps that have been added since the last notify() call. */
-    public ArrayList<ApplicationInfo> added = new ArrayList(DEFAULT_APPLICATIONS_NUMBER);
+    public ArrayList<ApplicationInfo> added =
+            new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
     /** The list of apps that have been removed since the last notify() call. */
-    public ArrayList<ApplicationInfo> removed = new ArrayList();
+    public ArrayList<ApplicationInfo> removed = new ArrayList<ApplicationInfo>();
     /** The list of apps that have been modified since the last notify() call. */
-    public ArrayList<ApplicationInfo> modified = new ArrayList();
+    public ArrayList<ApplicationInfo> modified = new ArrayList<ApplicationInfo>();
 
     /**
      * Boring constructor.
@@ -106,7 +96,7 @@
      */
     public void removePackage(String packageName) {
         final List<ApplicationInfo> data = this.data;
-        for (int i=data.size()-1; i>=0; i--) {
+        for (int i = data.size() - 1; i >= 0; i--) {
             ApplicationInfo info = data.get(i);
             final ComponentName component = info.intent.getComponent();
             if (packageName.equals(component.getPackageName())) {
@@ -126,7 +116,7 @@
         if (matches.size() > 0) {
             // Find disabled/removed activities and remove them from data and add them
             // to the removed list.
-            for (int i=data.size()-1; i>=0; i--) {
+            for (int i = data.size() - 1; i >= 0; i--) {
                 final ApplicationInfo applicationInfo = data.get(i);
                 final ComponentName component = applicationInfo.intent.getComponent();
                 if (packageName.equals(component.getPackageName())) {
@@ -142,7 +132,7 @@
             // Also updates existing activities with new labels/icons
             Utilities.BubbleText bubble = new Utilities.BubbleText(context);
             int count = matches.size();
-            for (int i=0; i<count; i++) {
+            for (int i = 0; i < count; i++) {
                 final ResolveInfo info = matches.get(i);
                 ApplicationInfo applicationInfo = findApplicationInfoLocked(
                         info.activityInfo.applicationInfo.packageName,
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index d29d721..1830521 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -2094,7 +2094,7 @@
      */
     public void bindPackageRemoved(String packageName, ArrayList<ApplicationInfo> apps) {
         removeDialog(DIALOG_CREATE_SHORTCUT);
-        mWorkspace.removeShortcutsForPackage(packageName);
+        mWorkspace.removeItemsForPackage(packageName);
         mAllAppsGrid.removeApps(apps);
     }
 
diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index 9b63524..a72e53a 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -18,7 +18,6 @@
 
 import android.app.Application;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index cafb9fa..29de3f7 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -18,18 +18,21 @@
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
+import android.os.RemoteException;
 import android.util.Log;
 import android.os.Process;
 import android.os.SystemClock;
@@ -602,6 +605,7 @@
                 final Context context = mContext;
                 final ContentResolver contentResolver = context.getContentResolver();
                 final PackageManager manager = context.getPackageManager();
+                final boolean isSafeMode = manager.isSafeMode();
 
                 /* TODO
                 if (mLocaleChanged) {
@@ -613,6 +617,8 @@
                 mAppWidgets.clear();
                 mFolders.clear();
 
+                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
+
                 final Cursor c = contentResolver.query(
                         LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
 
@@ -733,40 +739,50 @@
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
-
                                 id = c.getLong(idIndex);
-                                LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
+                                Uri uri = Uri.parse(c.getString(uriIndex));
 
-                                intentDescription = c.getString(intentIndex);
-                                intent = null;
-                                if (intentDescription != null) {
-                                    try {
-                                        intent = Intent.parseUri(intentDescription, 0);
-                                    } catch (URISyntaxException e) {
-                                        // Ignore, a live folder might not have a base intent
+                                // Make sure the live folder exists
+                                final ProviderInfo providerInfo =
+                                        context.getPackageManager().resolveContentProvider(
+                                                uri.getAuthority(), 0);
+
+                                if (providerInfo == null && !isSafeMode) {
+                                    itemsToRemove.add(id);
+                                } else {
+                                    LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
+    
+                                    intentDescription = c.getString(intentIndex);
+                                    intent = null;
+                                    if (intentDescription != null) {
+                                        try {
+                                            intent = Intent.parseUri(intentDescription, 0);
+                                        } catch (URISyntaxException e) {
+                                            // Ignore, a live folder might not have a base intent
+                                        }
                                     }
+    
+                                    liveFolderInfo.title = c.getString(titleIndex);
+                                    liveFolderInfo.id = id;
+                                    liveFolderInfo.uri = uri;
+                                    container = c.getInt(containerIndex);
+                                    liveFolderInfo.container = container;
+                                    liveFolderInfo.screen = c.getInt(screenIndex);
+                                    liveFolderInfo.cellX = c.getInt(cellXIndex);
+                                    liveFolderInfo.cellY = c.getInt(cellYIndex);
+                                    liveFolderInfo.baseIntent = intent;
+                                    liveFolderInfo.displayMode = c.getInt(displayModeIndex);
+    
+                                    loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
+                                            iconResourceIndex, liveFolderInfo);
+    
+                                    switch (container) {
+                                        case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+                                            mItems.add(liveFolderInfo);
+                                            break;
+                                    }
+                                    mFolders.put(liveFolderInfo.id, liveFolderInfo);
                                 }
-
-                                liveFolderInfo.title = c.getString(titleIndex);
-                                liveFolderInfo.id = id;
-                                container = c.getInt(containerIndex);
-                                liveFolderInfo.container = container;
-                                liveFolderInfo.screen = c.getInt(screenIndex);
-                                liveFolderInfo.cellX = c.getInt(cellXIndex);
-                                liveFolderInfo.cellY = c.getInt(cellYIndex);
-                                liveFolderInfo.uri = Uri.parse(c.getString(uriIndex));
-                                liveFolderInfo.baseIntent = intent;
-                                liveFolderInfo.displayMode = c.getInt(displayModeIndex);
-
-                                loadLiveFolderIcon(context, c, iconTypeIndex, iconPackageIndex,
-                                        iconResourceIndex, liveFolderInfo);
-
-                                switch (container) {
-                                    case LauncherSettings.Favorites.CONTAINER_DESKTOP:
-                                        mItems.add(liveFolderInfo);
-                                        break;
-                                }
-                                mFolders.put(liveFolderInfo.id, liveFolderInfo);
                                 break;
 
                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -798,6 +814,25 @@
                 } finally {
                     c.close();
                 }
+
+                if (itemsToRemove.size() > 0) {
+                    ContentProviderClient client = contentResolver.acquireContentProviderClient(
+                                    LauncherSettings.Favorites.CONTENT_URI);
+                    // Remove dead items
+                    for (long id : itemsToRemove) {
+                        if (DEBUG_LOADERS) {
+                            Log.d(TAG, "Removed id = " + id);
+                        }
+                        // Don't notify content observers
+                        try {
+                            client.delete(LauncherSettings.Favorites.getContentUri(id, false),
+                                    null, null);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Could not remove id = " + id);
+                        }
+                    }
+                }
+
                 if (DEBUG_LOADERS) {
                     Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
                 }
@@ -956,7 +991,7 @@
             private void bindAllApps() {
                 synchronized (mLock) {
                     final ArrayList<ApplicationInfo> results
-                            = (ArrayList<ApplicationInfo>)mAllAppsList.data.clone();
+                            = (ArrayList<ApplicationInfo>) mAllAppsList.data.clone();
                     // We're adding this now, so clear out this so we don't re-send them.
                     mAllAppsList.added = new ArrayList<ApplicationInfo>();
                     mHandler.post(new Runnable() {
@@ -971,7 +1006,7 @@
 
                             if (DEBUG_LOADERS) {
                                 Log.d(TAG, "bound app " + count + " icons in "
-                                    + (SystemClock.uptimeMillis()-t) + "ms");
+                                    + (SystemClock.uptimeMillis() - t) + "ms");
                             }
                         }
                     });
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index d068164..21f9070 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -20,15 +20,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ComponentName;
+import android.content.pm.ProviderInfo;
 import android.content.res.TypedArray;
 import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -1157,9 +1158,10 @@
         mAllowLongPress = allowLongPress;
     }
 
-    void removeShortcutsForPackage(String packageName) {
+    void removeItemsForPackage(String packageName) {
         final ArrayList<View> childrenToRemove = new ArrayList<View>();
         final int count = getChildCount();
+        final PackageManager manager = getContext().getPackageManager();
 
         for (int i = 0; i < count; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
@@ -1209,6 +1211,15 @@
                         final Folder folder = getOpenFolder();
                         if (folder != null) folder.notifyDataSetChanged();
                     }
+                } else if (tag instanceof LiveFolderInfo) {
+                    final LiveFolderInfo info = (LiveFolderInfo) tag;
+                    final Uri uri = info.uri;
+                    final ProviderInfo providerInfo = manager.resolveContentProvider(
+                            uri.getAuthority(), 0);
+                    if (providerInfo == null || packageName.equals(providerInfo.packageName)) {
+                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
+                        childrenToRemove.add(view);                        
+                    }
                 }
             }