Fixing issue where updating the visibility of one application can trigger all icons to disappear. (Bug 8757421)

- Also queueing bindComponentsRemoved() and bindPackagesUpdated() to wait for resume (Bug 8594153)

Change-Id: I44028fe79f6fa6bcd6b829e36f3f5b9ed756dc4d
diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java
index a3040d4..eda8c25 100644
--- a/src/com/android/launcher2/ApplicationInfo.java
+++ b/src/com/android/launcher2/ApplicationInfo.java
@@ -97,12 +97,6 @@
         firstInstallTime = info.firstInstallTime;
     }
 
-    /** Returns the package name that the shortcut's intent will resolve to, or an empty string if
-     *  none exists. */
-    String getPackageName() {
-        return super.getPackageName(intent);
-    }
-
     /**
      * Creates the application intent based on a component name and various launch flags.
      * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}.
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 5601169..901b661 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -1556,16 +1556,6 @@
         }
         return -1;
     }
-    private int findAppByPackage(List<ApplicationInfo> list, String packageName) {
-        int length = list.size();
-        for (int i = 0; i < length; ++i) {
-            ApplicationInfo info = list.get(i);
-            if (ItemInfo.getPackageName(info.intent).equals(packageName)) {
-                return i;
-            }
-        }
-        return -1;
-    }
     private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
         // loop through all the apps and remove apps that have the same component
         int length = list.size();
@@ -1577,18 +1567,8 @@
             }
         }
     }
-    private void removeAppsWithPackageNameWithoutInvalidate(ArrayList<String> packageNames) {
-        // loop through all the package names and remove apps that have the same package name
-        for (String pn : packageNames) {
-            int removeIndex = findAppByPackage(mApps, pn);
-            while (removeIndex > -1) {
-                mApps.remove(removeIndex);
-                removeIndex = findAppByPackage(mApps, pn);
-            }
-        }
-    }
-    public void removeApps(ArrayList<String> packageNames) {
-        removeAppsWithPackageNameWithoutInvalidate(packageNames);
+    public void removeApps(ArrayList<ApplicationInfo> appInfos) {
+        removeAppsWithoutInvalidate(appInfos);
         updatePageCounts();
         invalidateOnDataChange();
     }
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 78afe57..d15cb6e 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -328,18 +328,19 @@
         }
         endDrag();
     }
-    public void onAppsRemoved(ArrayList<String> packageNames, Context context) {
+    public void onAppsRemoved(ArrayList<ApplicationInfo> appInfos, Context context) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             Object rawDragInfo = mDragObject.dragInfo;
             if (rawDragInfo instanceof ShortcutInfo) {
                 ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
-                for (String pn : packageNames) {
+                for (ApplicationInfo info : appInfos) {
                     // Added null checks to prevent NPE we've seen in the wild
                     if (dragInfo != null &&
                         dragInfo.intent != null) {
-                        boolean isSamePackage = dragInfo.getPackageName().equals(pn);
-                        if (isSamePackage) {
+                        boolean isSameComponent =
+                                dragInfo.intent.getComponent().equals(info.componentName);
+                        if (isSameComponent) {
                             cancelDrag();
                             return;
                         }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 0aab2ac..5ed7f6b 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -3704,27 +3704,51 @@
     }
 
     /**
-     * A package was uninstalled.
+     * A package was uninstalled.  We take both the super set of packageNames
+     * in addition to specific applications to remove, the reason being that
+     * this can be called when a package is updated as well.  In that scenario,
+     * we only remove specific components from the workspace, where as
+     * package-removal should clear all items by package name.
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void bindAppsRemoved(ArrayList<String> packageNames, boolean permanent) {
-        if (permanent) {
-            mWorkspace.removeItems(packageNames);
+    public void bindComponentsRemoved(final ArrayList<String> packageNames,
+                                      final ArrayList<ApplicationInfo> appInfos,
+                                      final boolean matchPackageNamesOnly) {
+        if (waitUntilResume(new Runnable() {
+            public void run() {
+                bindComponentsRemoved(packageNames, appInfos, matchPackageNamesOnly);
+            }
+        })) {
+            return;
+        }
+
+        if (matchPackageNamesOnly) {
+            mWorkspace.removeItemsByPackageName(packageNames);
+        } else {
+            mWorkspace.removeItemsByApplicationInfo(appInfos);
         }
 
         if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.removeApps(packageNames);
+            mAppsCustomizeContent.removeApps(appInfos);
         }
 
         // Notify the drag controller
-        mDragController.onAppsRemoved(packageNames, this);
+        mDragController.onAppsRemoved(appInfos, this);
     }
 
     /**
      * A number of packages were updated.
      */
     public void bindPackagesUpdated() {
+        if (waitUntilResume(new Runnable() {
+            public void run() {
+                bindPackagesUpdated();
+            }
+        })) {
+            return;
+        }
+
         if (mAppsCustomizeContent != null) {
             mAppsCustomizeContent.onPackagesUpdated();
         }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 99808a6..00770b2 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -53,6 +53,7 @@
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
 import java.text.Collator;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -156,7 +157,9 @@
         public void bindAllApplications(ArrayList<ApplicationInfo> apps);
         public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
         public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
-        public void bindAppsRemoved(ArrayList<String> packageNames, boolean permanent);
+        public void bindComponentsRemoved(ArrayList<String> packageNames,
+                        ArrayList<ApplicationInfo> appInfos,
+                        boolean matchPackageNamesOnly);
         public void bindPackagesUpdated();
         public boolean isAllAppsVisible();
         public boolean isAllAppsButtonRank(int rank);
@@ -2009,6 +2012,7 @@
 
             ArrayList<ApplicationInfo> added = null;
             ArrayList<ApplicationInfo> modified = null;
+            final ArrayList<ApplicationInfo> removedApps = new ArrayList<ApplicationInfo>();
 
             if (mBgAllAppsList.added.size() > 0) {
                 added = new ArrayList<ApplicationInfo>(mBgAllAppsList.added);
@@ -2018,16 +2022,9 @@
                 modified = new ArrayList<ApplicationInfo>(mBgAllAppsList.modified);
                 mBgAllAppsList.modified.clear();
             }
-            // We may be removing packages that have no associated launcher application, so we
-            // pass through the removed package names directly.
-            // NOTE: We flush the icon cache aggressively in removePackage() above.
-            final ArrayList<String> removedPackageNames = new ArrayList<String>();
             if (mBgAllAppsList.removed.size() > 0) {
+                removedApps.addAll(mBgAllAppsList.removed);
                 mBgAllAppsList.removed.clear();
-
-                for (int i = 0; i < N; ++i) {
-                    removedPackageNames.add(packages[i]);
-                }
             }
 
             final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
@@ -2058,13 +2055,19 @@
                     }
                 });
             }
-            if (!removedPackageNames.isEmpty()) {
-                final boolean permanent = mOp != OP_UNAVAILABLE;
+            // If a package has been removed, or an app has been removed as a result of
+            // an update (for example), make the removed callback.
+            if (mOp == OP_REMOVE || !removedApps.isEmpty()) {
+                final boolean permanent = (mOp == OP_REMOVE);
+                final ArrayList<String> removedPackageNames =
+                        new ArrayList<String>(Arrays.asList(packages));
+
                 mHandler.post(new Runnable() {
                     public void run() {
                         Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindAppsRemoved(removedPackageNames, permanent);
+                            callbacks.bindComponentsRemoved(removedPackageNames,
+                                    removedApps, permanent);
                         }
                     }
                 });
diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java
index 968d3b8..ccb663a 100644
--- a/src/com/android/launcher2/ShortcutInfo.java
+++ b/src/com/android/launcher2/ShortcutInfo.java
@@ -93,12 +93,6 @@
         return mIcon;
     }
 
-    /** Returns the package name that the shortcut's intent will resolve to, or an empty string if
-     *  none exists. */
-    String getPackageName() {
-        return super.getPackageName(intent);
-    }
-
     public void updateIcon(IconCache iconCache) {
         mIcon = iconCache.getIcon(intent);
         usingFallbackIcon = iconCache.isDefaultIcon(mIcon);
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index d4488db..ac4c3dd 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -3639,10 +3639,66 @@
         }
     }
 
-    void removeItems(final ArrayList<String> packages) {
-        final HashSet<String> packageNames = new HashSet<String>();
+    // Removes ALL items that match a given package name, this is usually called when a package
+    // has been removed and we want to remove all components (widgets, shortcuts, apps) that
+    // belong to that package.
+    void removeItemsByPackageName(final ArrayList<String> packages) {
+        HashSet<String> packageNames = new HashSet<String>();
         packageNames.addAll(packages);
 
+        // Just create a hash table of all the specific components that this will affect
+        HashSet<ComponentName> cns = new HashSet<ComponentName>();
+        ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
+        for (CellLayout layoutParent : cellLayouts) {
+            ViewGroup layout = layoutParent.getShortcutsAndWidgets();
+            int childCount = layout.getChildCount();
+            for (int i = 0; i < childCount; ++i) {
+                View view = layout.getChildAt(i);
+                Object tag = view.getTag();
+
+                if (tag instanceof ShortcutInfo) {
+                    ShortcutInfo info = (ShortcutInfo) tag;
+                    ComponentName cn = info.intent.getComponent();
+                    if (packageNames.contains(cn.getPackageName())) {
+                        cns.add(cn);
+                    }
+                } else if (tag instanceof FolderInfo) {
+                    FolderInfo info = (FolderInfo) tag;
+                    for (ShortcutInfo s : info.contents) {
+                        ComponentName cn = s.intent.getComponent();
+                        if (packageNames.contains(cn.getPackageName())) {
+                            cns.add(cn);
+                        }
+                    }
+                } else if (tag instanceof LauncherAppWidgetInfo) {
+                    LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
+                    ComponentName cn = info.providerName;
+                    if (packageNames.contains(cn.getPackageName())) {
+                        cns.add(cn);
+                    }
+                }
+            }
+        }
+
+        // Remove all the things
+        removeItemsByComponentName(cns);
+    }
+
+    // Removes items that match the application info specified, when applications are removed
+    // as a part of an update, this is called to ensure that other widgets and application
+    // shortcuts are not removed.
+    void removeItemsByApplicationInfo(final ArrayList<ApplicationInfo> appInfos) {
+        // Just create a hash table of all the specific components that this will affect
+        HashSet<ComponentName> cns = new HashSet<ComponentName>();
+        for (ApplicationInfo info : appInfos) {
+            cns.add(info.componentName);
+        }
+
+        // Remove all the things
+        removeItemsByComponentName(cns);
+    }
+
+    void removeItemsByComponentName(final HashSet<ComponentName> componentNames) {
         ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
         for (final CellLayout layoutParent: cellLayouts) {
             final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
@@ -3664,7 +3720,7 @@
                             final ComponentName name = intent.getComponent();
 
                             if (name != null) {
-                                if (packageNames.contains(name.getPackageName())) {
+                                if (componentNames.contains(name)) {
                                     LauncherModel.deleteItemFromDatabase(mLauncher, info);
                                     childrenToRemove.add(view);
                                 }
@@ -3682,7 +3738,7 @@
                                 final ComponentName name = intent.getComponent();
 
                                 if (name != null) {
-                                    if (packageNames.contains(name.getPackageName())) {
+                                    if (componentNames.contains(name)) {
                                         appsToRemoveFromFolder.add(appInfo);
                                     }
                                 }
@@ -3695,7 +3751,7 @@
                             final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) tag;
                             final ComponentName provider = info.providerName;
                             if (provider != null) {
-                                if (packageNames.contains(provider.getPackageName())) {
+                                if (componentNames.contains(provider)) {
                                     LauncherModel.deleteItemFromDatabase(mLauncher, info);
                                     childrenToRemove.add(view);
                                 }
@@ -3740,8 +3796,7 @@
                         while (iter.hasNext()) {
                             try {
                                 Intent intent = Intent.parseUri(iter.next(), 0);
-                                String pn = ItemInfo.getPackageName(intent);
-                                if (packageNames.contains(pn)) {
+                                if (componentNames.contains(intent.getComponent())) {
                                     iter.remove();
                                 }