Updating icons for sortcuts when the target app updates.

Bug: 17398260
Change-Id: I055abb971d1f72245e8616ac2ce07bcdf37cdd52
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 38d2fa5..ac9a125 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -18,10 +18,6 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -101,10 +97,8 @@
         final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
                 user);
 
-        if (matches.size() > 0) {
-            for (LauncherActivityInfoCompat info : matches) {
-                add(new AppInfo(context, info, user, mIconCache, null));
-            }
+        for (LauncherActivityInfoCompat info : matches) {
+            add(new AppInfo(context, info, user, mIconCache, null));
         }
     }
 
@@ -149,9 +143,7 @@
 
             // Find enabled activities and add them to the adapter
             // Also updates existing activities with new labels/icons
-            int count = matches.size();
-            for (int i = 0; i < count; i++) {
-                final LauncherActivityInfoCompat info = matches.get(i);
+            for (final LauncherActivityInfoCompat info : matches) {
                 AppInfo applicationInfo = findApplicationInfoLocked(
                         info.getComponentName().getPackageName(), user,
                         info.getComponentName().getClassName());
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 47cca37..06f9f29 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -121,7 +121,7 @@
                 android.R.mipmap.sym_def_app_icon);
     }
 
-    public Drawable getFullResIcon(Resources resources, int iconId) {
+    private Drawable getFullResIcon(Resources resources, int iconId) {
         Drawable d;
         try {
             d = resources.getDrawableForDensity(iconId, mIconDpi);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3b903c0..aa403db 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -4719,6 +4719,26 @@
     }
 
     /**
+     * Some shortcuts were updated in the background.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     */
+    public void bindShortcutsUpdated(final ArrayList<ShortcutInfo> shortcuts) {
+        Runnable r = new Runnable() {
+            public void run() {
+                bindShortcutsUpdated(shortcuts);
+            }
+        };
+        if (waitUntilResume(r)) {
+            return;
+        }
+
+        if (mWorkspace != null) {
+            mWorkspace.updateShortcuts(shortcuts);
+        }
+    }
+
+    /**
      * Update the state of a package, typically related to install state.
      *
      * Implementation of the method from LauncherModel.Callbacks.
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 550535e..eb4210f 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -198,6 +198,7 @@
                                   ArrayList<ItemInfo> addAnimated,
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
+        public void bindShortcutsUpdated(ArrayList<ShortcutInfo> shortcuts);
         public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
         public void updatePackageBadge(String packageName);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
@@ -3059,6 +3060,43 @@
                 });
             }
 
+            // Update shortcuts which use an iconResource
+            if (mOp == OP_ADD || mOp == OP_UPDATE) {
+                final ArrayList<ShortcutInfo> iconsChanged = new ArrayList<ShortcutInfo>();
+                HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
+                // We need to iteration over the items here, so that we can avoid new Bitmap
+                // creation on the UI thread.
+                synchronized (sBgLock) {
+                    for (ItemInfo info : sBgWorkspaceItems) {
+                        if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
+                            ShortcutInfo si = (ShortcutInfo) info;
+                            if ((si.iconResource != null)
+                                    && packageSet.contains(si.getTargetComponent().getPackageName())){
+                                Bitmap icon = Utilities.createIconBitmap(si.iconResource.packageName,
+                                        si.iconResource.resourceName, mIconCache, context);
+                                if (icon != null) {
+                                    si.setIcon(icon);
+                                    si.usingFallbackIcon = false;
+                                    iconsChanged.add(si);
+                                    updateItemInDatabase(context, si);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (!iconsChanged.isEmpty()) {
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+                            if (callbacks == cb && cb != null) {
+                                callbacks.bindShortcutsUpdated(iconsChanged);
+                            }
+                        }
+                    });
+                }
+            }
+
             final ArrayList<String> removedPackageNames =
                     new ArrayList<String>();
             if (mOp == OP_REMOVE) {
@@ -3385,19 +3423,9 @@
         case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
             String packageName = c.getString(iconPackageIndex);
             String resourceName = c.getString(iconResourceIndex);
-            PackageManager packageManager = context.getPackageManager();
             info.customIcon = false;
             // the resource
-            try {
-                Resources resources = packageManager.getResourcesForApplication(packageName);
-                if (resources != null) {
-                    final int id = resources.getIdentifier(resourceName, null, null);
-                    icon = Utilities.createIconBitmap(
-                            mIconCache.getFullResIcon(resources, id), context);
-                }
-            } catch (Exception e) {
-                // drop this.  we have other places to look for icons
-            }
+            icon = Utilities.createIconBitmap(packageName, resourceName, mIconCache, context);
             // the db
             if (icon == null) {
                 icon = getIconFromCursor(c, iconIndex, context);
@@ -3490,19 +3518,10 @@
             customIcon = true;
         } else {
             Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-            if (extra != null && extra instanceof ShortcutIconResource) {
-                try {
-                    iconResource = (ShortcutIconResource) extra;
-                    final PackageManager packageManager = context.getPackageManager();
-                    Resources resources = packageManager.getResourcesForApplication(
-                            iconResource.packageName);
-                    final int id = resources.getIdentifier(iconResource.resourceName, null, null);
-                    icon = Utilities.createIconBitmap(
-                            mIconCache.getFullResIcon(resources, id),
-                            context);
-                } catch (Exception e) {
-                    Log.w(TAG, "Could not load shortcut icon: " + extra);
-                }
+            if (extra instanceof ShortcutIconResource) {
+                iconResource = (ShortcutIconResource) extra;
+                icon = Utilities.createIconBitmap(iconResource.packageName,
+                        iconResource.resourceName, mIconCache, context);
             }
         }
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 84eb8bf..6caa1cf 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -108,6 +108,27 @@
     }
 
     /**
+     * Returns a bitmap suitable for the all apps view. If the package or the resource do not
+     * exist, it returns null.
+     */
+    static Bitmap createIconBitmap(String packageName, String resourceName, IconCache cache,
+            Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        // the resource
+        try {
+            Resources resources = packageManager.getResourcesForApplication(packageName);
+            if (resources != null) {
+                final int id = resources.getIdentifier(resourceName, null, null);
+                return createIconBitmap(
+                        resources.getDrawableForDensity(id, cache.getFullResIconDpi()), context);
+            }
+        } catch (Exception e) {
+            // Icon not found.
+        }
+        return null;
+    }
+
+    /**
      * Returns a bitmap which is of the appropriate size to be displayed as an icon
      */
     static Bitmap createIconBitmap(Bitmap icon, Context context) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 774996e..5951be6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4800,6 +4800,28 @@
         }
     }
 
+
+    void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
+        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
+        mapOverItems(MAP_RECURSE, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v, View parent) {
+                if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
+                        updates.contains(info)) {
+                    ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+                    BubbleTextView shortcut = (BubbleTextView) v;
+                    shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
+
+                    if (parent != null) {
+                        parent.invalidate();
+                    }
+                }
+                // process all the shortcuts
+                return false;
+            }
+        });
+    }
+
     void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) {
         // Break the appinfo list per user
         final HashMap<UserHandleCompat, ArrayList<AppInfo>> appsPerUser =