Remove widgets when the corresponding apk is uninstalled.
Bug #2298872
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index 25db72b..a28973b 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -25,7 +25,8 @@
 class LauncherAppWidgetInfo extends ItemInfo {
 
     /**
-     * Identifier for this widget when talking with {@link AppWidgetManager} for updates.
+     * Identifier for this widget when talking with
+     * {@link android.appwidget.AppWidgetManager} for updates.
      */
     int appWidgetId;
     
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 29de3f7..7eb240f 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher2;
 
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
@@ -605,6 +607,7 @@
                 final Context context = mContext;
                 final ContentResolver contentResolver = context.getContentResolver();
                 final PackageManager manager = context.getPackageManager();
+                final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
                 final boolean isSafeMode = manager.isSafeMode();
 
                 /* TODO
@@ -788,23 +791,33 @@
                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                                 // Read all Launcher-specific widget details
                                 int appWidgetId = c.getInt(appWidgetIdIndex);
-                                appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
-                                appWidgetInfo.id = c.getLong(idIndex);
-                                appWidgetInfo.screen = c.getInt(screenIndex);
-                                appWidgetInfo.cellX = c.getInt(cellXIndex);
-                                appWidgetInfo.cellY = c.getInt(cellYIndex);
-                                appWidgetInfo.spanX = c.getInt(spanXIndex);
-                                appWidgetInfo.spanY = c.getInt(spanYIndex);
+                                id = c.getLong(idIndex);
 
-                                container = c.getInt(containerIndex);
-                                if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                    Log.e(TAG, "Widget found where container "
-                                            + "!= CONTAINER_DESKTOP -- ignoring!");
-                                    continue;
+                                final AppWidgetProviderInfo provider =
+                                        widgets.getAppWidgetInfo(appWidgetId);
+                                
+                                if (!isSafeMode && (provider == null || provider.provider == null ||
+                                        provider.provider.getPackageName() == null)) {
+                                    itemsToRemove.add(id);
+                                } else {
+                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
+                                    appWidgetInfo.id = id;
+                                    appWidgetInfo.screen = c.getInt(screenIndex);
+                                    appWidgetInfo.cellX = c.getInt(cellXIndex);
+                                    appWidgetInfo.cellY = c.getInt(cellYIndex);
+                                    appWidgetInfo.spanX = c.getInt(spanXIndex);
+                                    appWidgetInfo.spanY = c.getInt(spanYIndex);
+    
+                                    container = c.getInt(containerIndex);
+                                    if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                                        Log.e(TAG, "Widget found where container "
+                                                + "!= CONTAINER_DESKTOP -- ignoring!");
+                                        continue;
+                                    }
+                                    appWidgetInfo.container = c.getInt(containerIndex);
+    
+                                    mAppWidgets.add(appWidgetInfo);
                                 }
-                                appWidgetInfo.container = c.getInt(containerIndex);
-
-                                mAppWidgets.add(appWidgetInfo);
                                 break;
                             }
                         } catch (Exception e) {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 21f9070..9abf562 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -17,6 +17,8 @@
 package com.android.launcher2;
 
 import android.app.WallpaperManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ComponentName;
@@ -1158,84 +1160,106 @@
         mAllowLongPress = allowLongPress;
     }
 
-    void removeItemsForPackage(String packageName) {
-        final ArrayList<View> childrenToRemove = new ArrayList<View>();
+    void removeItemsForPackage(final String packageName) {
         final int count = getChildCount();
         final PackageManager manager = getContext().getPackageManager();
+        final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
 
         for (int i = 0; i < count; i++) {
             final CellLayout layout = (CellLayout) getChildAt(i);
-            int childCount = layout.getChildCount();
 
-            childrenToRemove.clear();
+            // Avoid ANRs by treating each screen separately
+            post(new Runnable() {
+                public void run() {
+                    final ArrayList<View> childrenToRemove = new ArrayList<View>();
+                    childrenToRemove.clear();
+        
+                    int childCount = layout.getChildCount();
+                    for (int j = 0; j < childCount; j++) {
+                        final View view = layout.getChildAt(j);
+                        Object tag = view.getTag();
+        
+                        if (tag instanceof ApplicationInfo) {
+                            final ApplicationInfo info = (ApplicationInfo) tag;
+                            // We need to check for ACTION_MAIN otherwise getComponent() might
+                            // return null for some shortcuts (for instance, for shortcuts to
+                            // web pages.)
+                            final Intent intent = info.intent;
+                            final ComponentName name = intent.getComponent();
+        
+                            if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
+                                    name != null && packageName.equals(name.getPackageName())) {
+                                // TODO: This should probably be done on a worker thread
+                                LauncherModel.deleteItemFromDatabase(mLauncher, info);
+                                childrenToRemove.add(view);
+                            }
+                        } else if (tag instanceof UserFolderInfo) {
+                            final UserFolderInfo info = (UserFolderInfo) tag;
+                            final ArrayList<ApplicationInfo> contents = info.contents;
+                            final ArrayList<ApplicationInfo> toRemove =
+                                    new ArrayList<ApplicationInfo>(1);
+                            final int contentsCount = contents.size();
+                            boolean removedFromFolder = false;
+        
+                            for (int k = 0; k < contentsCount; k++) {
+                                final ApplicationInfo appInfo = contents.get(k);
+                                final Intent intent = appInfo.intent;
+                                final ComponentName name = intent.getComponent();
+        
+                                if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
+                                        name != null && packageName.equals(name.getPackageName())) {
+                                    toRemove.add(appInfo);
+                                    // TODO: This should probably be done on a worker thread
+                                    LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
+                                    removedFromFolder = true;
+                                }
+                            }
+        
+                            contents.removeAll(toRemove);
+                            if (removedFromFolder) {
+                                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);
 
-            for (int j = 0; j < childCount; j++) {
-                final View view = layout.getChildAt(j);
-                Object tag = view.getTag();
-
-                if (tag instanceof ApplicationInfo) {
-                    final ApplicationInfo info = (ApplicationInfo) tag;
-                    // We need to check for ACTION_MAIN otherwise getComponent() might
-                    // return null for some shortcuts (for instance, for shortcuts to
-                    // web pages.)
-                    final Intent intent = info.intent;
-                    final ComponentName name = intent.getComponent();
-
-                    if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
-                            name != null && packageName.equals(name.getPackageName())) {
-                        LauncherModel.deleteItemFromDatabase(mLauncher, info);
-                        childrenToRemove.add(view);
-                    }
-                } else if (tag instanceof UserFolderInfo) {
-                    final UserFolderInfo info = (UserFolderInfo) tag;
-                    final ArrayList<ApplicationInfo> contents = info.contents;
-                    final ArrayList<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(1);
-                    final int contentsCount = contents.size();
-                    boolean removedFromFolder = false;
-
-                    for (int k = 0; k < contentsCount; k++) {
-                        final ApplicationInfo appInfo = contents.get(k);
-                        final Intent intent = appInfo.intent;
-                        final ComponentName name = intent.getComponent();
-
-                        if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
-                                name != null && packageName.equals(name.getPackageName())) {
-                            toRemove.add(appInfo);
-                            LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
-                            removedFromFolder = true;
+                            if (providerInfo == null ||
+                                    packageName.equals(providerInfo.packageName)) {
+                                // TODO: This should probably be done on a worker thread
+                                LauncherModel.deleteItemFromDatabase(mLauncher, info);
+                                childrenToRemove.add(view);                        
+                            }
+                        } else if (tag instanceof LauncherAppWidgetInfo) {
+                            final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
+                            final AppWidgetProviderInfo provider =
+                                    widgets.getAppWidgetInfo(info.appWidgetId);
+                            if (provider == null ||
+                                    packageName.equals(provider.provider.getPackageName())) {
+                                // TODO: This should probably be done on a worker thread
+                                LauncherModel.deleteItemFromDatabase(mLauncher, info);
+                                childrenToRemove.add(view);                                
+                            }
                         }
                     }
-
-                    contents.removeAll(toRemove);
-                    if (removedFromFolder) {
-                        final Folder folder = getOpenFolder();
-                        if (folder != null) folder.notifyDataSetChanged();
+        
+                    childCount = childrenToRemove.size();
+                    for (int j = 0; j < childCount; j++) {
+                        View child = childrenToRemove.get(j);
+                        layout.removeViewInLayout(child);
+                        if (child instanceof DropTarget) {
+                            mDragController.removeDropTarget((DropTarget)child);
+                        }
                     }
-                } 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);                        
+        
+                    if (childCount > 0) {
+                        layout.requestLayout();
+                        layout.invalidate();
                     }
                 }
-            }
-
-            childCount = childrenToRemove.size();
-            for (int j = 0; j < childCount; j++) {
-                View child = childrenToRemove.get(j);
-                layout.removeViewInLayout(child);
-                if (child instanceof DropTarget) {
-                    mDragController.removeDropTarget((DropTarget)child);
-                }
-            }
-
-            if (childCount > 0) {
-                layout.requestLayout();
-                layout.invalidate();
-            }
+            });
         }
     }