Merge "Ensuring that we have a valid name for installed shortcuts. (Bug 11266005)" into jb-ub-now-jetsonic
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 606a80d..d78b536 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -104,7 +104,7 @@
     <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
     <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Unda skrini zaidi za programu na folda"</string>
     <string name="workspace_cling_title" msgid="5626202359865825661">"Panga nafasi yako"</string>
-    <string name="workspace_cling_move_item" msgid="528201129978005352">"Gusa na ushikile mandharinyuma ili udhibiti mandhari, wijeti, na mipangilio."</string>
+    <string name="workspace_cling_move_item" msgid="528201129978005352">"Gusa na ushikilie mandharinyuma ili udhibiti mandhari, wijeti, na mipangilio."</string>
     <string name="all_apps_cling_title" msgid="34929250753095858">"Chagua programu kadhaa"</string>
     <string name="all_apps_cling_add_item" msgid="400866858451850784">"Ili kuongeza programu kwenye Skrini yako Kuu, iguse na uishikilie."</string>
     <string name="folder_cling_title" msgid="3894908818693254164">"Folda hii hapa"</string>
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 5b5c35c..5e733f0 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -25,14 +26,8 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.*;
 import android.view.inputmethod.InputMethodManager;
-
 import com.android.launcher3.R;
 
 import java.util.ArrayList;
@@ -323,7 +318,7 @@
         }
         endDrag();
     }
-    public void onAppsRemoved(ArrayList<AppInfo> appInfos, Context context) {
+    public void onAppsRemoved(final ArrayList<String> packageNames, ArrayList<AppInfo> appInfos) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             Object rawDragInfo = mDragObject.dragInfo;
@@ -333,8 +328,9 @@
                     // Added null checks to prevent NPE we've seen in the wild
                     if (dragInfo != null &&
                         dragInfo.intent != null) {
-                        boolean isSameComponent =
-                                dragInfo.intent.getComponent().equals(info.componentName);
+                        ComponentName cn = dragInfo.intent.getComponent();
+                        boolean isSameComponent = cn.equals(info.componentName) ||
+                                packageNames.contains(cn.getPackageName());
                         if (isSameComponent) {
                             cancelDrag();
                             return;
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 5ae6c34..835c472 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -36,6 +36,9 @@
 import org.json.*;
 
 public class InstallShortcutReceiver extends BroadcastReceiver {
+    private static final String TAG = "InstallShortcutReceiver";
+    private static final boolean DBG = false;
+
     public static final String ACTION_INSTALL_SHORTCUT =
             "com.android.launcher.action.INSTALL_SHORTCUT";
 
@@ -94,21 +97,31 @@
                 }
                 json = json.endObject();
                 SharedPreferences.Editor editor = sharedPrefs.edit();
+                if (DBG) Log.d(TAG, "Adding to APPS_PENDING_INSTALL: " + json);
                 addToStringSet(sharedPrefs, editor, APPS_PENDING_INSTALL, json.toString());
                 editor.commit();
             } catch (org.json.JSONException e) {
-                Log.d("InstallShortcutReceiver", "Exception when adding shortcut: " + e);
+                Log.d(TAG, "Exception when adding shortcut: " + e);
             }
         }
     }
 
     public static void removeFromInstallQueue(SharedPreferences sharedPrefs,
                                               ArrayList<String> packageNames) {
+        if (packageNames.isEmpty()) {
+            return;
+        }
         synchronized(sLock) {
             Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
+            if (DBG) {
+                Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
+                        + ", removing packages: " + packageNames);
+            }
             if (strings != null) {
                 Set<String> newStrings = new HashSet<String>(strings);
-                for (String json : newStrings) {
+                Iterator<String> newStringsIter = newStrings.iterator();
+                while (newStringsIter.hasNext()) {
+                    String json = newStringsIter.next();
                     try {
                         JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
                         Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0);
@@ -117,12 +130,12 @@
                             pn = launchIntent.getComponent().getPackageName();
                         }
                         if (packageNames.contains(pn)) {
-                            newStrings.remove(json);
+                            newStringsIter.remove();
                         }
                     } catch (org.json.JSONException e) {
-                        Log.d("InstallShortcutReceiver", "Exception reading shortcut to remove: " + e);
+                        Log.d(TAG, "Exception reading shortcut to remove: " + e);
                     } catch (java.net.URISyntaxException e) {
-                        Log.d("InstallShortcutReceiver", "Exception reading shortcut to remove: " + e);
+                        Log.d(TAG, "Exception reading shortcut to remove: " + e);
                     }
                 }
                 sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL,
@@ -135,6 +148,7 @@
             SharedPreferences sharedPrefs) {
         synchronized(sLock) {
             Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
+            if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
             if (strings == null) {
                 return new ArrayList<PendingInstallShortcutInfo>();
             }
@@ -167,11 +181,9 @@
                         new PendingInstallShortcutInfo(data, name, launchIntent);
                     infos.add(info);
                 } catch (org.json.JSONException e) {
-                    Log.d("InstallShortcutReceiver",
-                            "Exception reading shortcut to add: " + e);
+                    Log.d(TAG, "Exception reading shortcut to add: " + e);
                 } catch (java.net.URISyntaxException e) {
-                    Log.d("InstallShortcutReceiver",
-                            "Exception reading shortcut to add: " + e);
+                    Log.d(TAG, "Exception reading shortcut to add: " + e);
                 }
             }
             sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).commit();
@@ -203,6 +215,8 @@
             return;
         }
 
+        if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0));
+
         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
         if (intent == null) {
             return;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b0e4968..af58f79 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -3971,26 +3971,27 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindComponentsRemoved(final ArrayList<String> packageNames,
-                                      final ArrayList<AppInfo> appInfos,
-                                      final boolean packageRemoved) {
+                                      final ArrayList<AppInfo> appInfos) {
         Runnable r = new Runnable() {
             public void run() {
-                bindComponentsRemoved(packageNames, appInfos, packageRemoved);
+                bindComponentsRemoved(packageNames, appInfos);
             }
         };
         if (waitUntilResume(r)) {
             return;
         }
 
-        if (packageRemoved) {
+        if (!packageNames.isEmpty()) {
             mWorkspace.removeItemsByPackageName(packageNames);
-        } else {
+        }
+        if (!appInfos.isEmpty()) {
             mWorkspace.removeItemsByApplicationInfo(appInfos);
         }
 
         // Notify the drag controller
-        mDragController.onAppsRemoved(appInfos, this);
+        mDragController.onAppsRemoved(packageNames, appInfos);
 
+        // Update AllApps
         if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
                 mAppsCustomizeContent != null) {
             mAppsCustomizeContent.removeApps(appInfos);
@@ -4007,7 +4008,6 @@
                 mWidgetsAndShortcuts = null;
             }
         };
-
     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
             mWidgetsAndShortcuts = widgetsAndShortcuts;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index aad768d..c746b4d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -165,8 +165,7 @@
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
-                        ArrayList<AppInfo> appInfos,
-                        boolean matchPackageNamesOnly);
+                        ArrayList<AppInfo> appInfos);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
         public boolean isAllAppsButtonRank(int rank);
@@ -2553,43 +2552,47 @@
                     }
                 });
             }
-            // 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 packageRemoved = (mOp == OP_REMOVE);
-                final ArrayList<String> removedPackageNames =
-                        new ArrayList<String>(Arrays.asList(packages));
 
-                // Update the launcher db to reflect the removal of apps
-                if (packageRemoved) {
-                    for (String pn : removedPackageNames) {
-                        ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
-                        for (ItemInfo i : infos) {
-                            deleteItemFromDatabase(context, i);
-                        }
-                    }
-
-                    // Remove any queued items from the install queue
-                    String spKey = LauncherAppState.getSharedPreferencesKey();
-                    SharedPreferences sp =
-                            context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
-                    InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
-                } else {
-                    for (AppInfo a : removedApps) {
-                        ArrayList<ItemInfo> infos =
-                                getItemInfoForComponentName(a.componentName);
-                        for (ItemInfo i : infos) {
-                            deleteItemFromDatabase(context, i);
-                        }
+            final ArrayList<String> removedPackageNames =
+                    new ArrayList<String>();
+            if (mOp == OP_REMOVE) {
+                // Mark all packages in the broadcast to be removed
+                removedPackageNames.addAll(Arrays.asList(packages));
+            } else if (mOp == OP_UPDATE) {
+                // Mark disabled packages in the broadcast to be removed
+                final PackageManager pm = context.getPackageManager();
+                for (int i=0; i<N; i++) {
+                    if (isPackageDisabled(pm, packages[i])) {
+                        removedPackageNames.add(packages[i]);
                     }
                 }
-
+            }
+            // Remove all the components associated with this package
+            for (String pn : removedPackageNames) {
+                ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
+                for (ItemInfo i : infos) {
+                    deleteItemFromDatabase(context, i);
+                }
+            }
+            // Remove all the specific components
+            for (AppInfo a : removedApps) {
+                ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName);
+                for (ItemInfo i : infos) {
+                    deleteItemFromDatabase(context, i);
+                }
+            }
+            if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
+                // Remove any queued items from the install queue
+                String spKey = LauncherAppState.getSharedPreferencesKey();
+                SharedPreferences sp =
+                        context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+                InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
+                // Call the components-removed callback
                 mHandler.post(new Runnable() {
                     public void run() {
                         Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindComponentsRemoved(removedPackageNames,
-                                    removedApps, packageRemoved);
+                            callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
                         }
                     }
                 });
@@ -2631,19 +2634,26 @@
         return widgetsAndShortcuts;
     }
 
+    private boolean isPackageDisabled(PackageManager pm, String packageName) {
+        try {
+            PackageInfo pi = pm.getPackageInfo(packageName, 0);
+            return !pi.applicationInfo.enabled;
+        } catch (NameNotFoundException e) {
+            // Fall through
+        }
+        return false;
+    }
     private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
         if (cn == null) {
             return false;
         }
+        if (isPackageDisabled(pm, cn.getPackageName())) {
+            return false;
+        }
 
         try {
-            // Skip if the application is disabled
-            PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
-            if (!pi.applicationInfo.enabled) {
-                return false;
-            }
-
             // Check the activity
+            PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
             return (pm.getActivityInfo(cn, 0) != null);
         } catch (NameNotFoundException e) {
             return false;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2fdcb43..a1353c3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4381,29 +4381,45 @@
         stripEmptyScreens();
     }
 
+    private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
+                                View child) {
+        ComponentName cn = info.getIntent().getComponent();
+        if (cn != null) {
+            AppInfo appInfo = appsMap.get(info.getIntent().getComponent());
+            if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
+                ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+                BubbleTextView shortcut = (BubbleTextView) child;
+                shortcutInfo.updateIcon(mIconCache);
+                shortcutInfo.title = appInfo.title.toString();
+                shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
+            }
+        }
+    }
+
     void updateShortcuts(ArrayList<AppInfo> apps) {
+        // Create a map of the apps to test against
+        final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
+        for (AppInfo ai : apps) {
+            appsMap.put(ai.componentName, ai);
+        }
+
         ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            int childCount = layout.getChildCount();
-            for (int j = 0; j < childCount; j++) {
-                final View view = layout.getChildAt(j);
-                Object tag = view.getTag();
-
-                if (LauncherModel.isShortcutInfoUpdateable((ItemInfo) tag)) {
-                    ShortcutInfo info = (ShortcutInfo) tag;
-
-                    final Intent intent = info.intent;
-                    final ComponentName name = intent.getComponent();
-                    final int appCount = apps.size();
-                    for (int k = 0; k < appCount; k++) {
-                        AppInfo app = apps.get(k);
-                        if (app.componentName.equals(name)) {
-                            BubbleTextView shortcut = (BubbleTextView) view;
-                            info.updateIcon(mIconCache);
-                            info.title = app.title.toString();
-                            shortcut.applyFromShortcutInfo(info, mIconCache);
-                        }
+            // Update all the children shortcuts
+            final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
+            for (int j = 0; j < layout.getChildCount(); j++) {
+                View v = layout.getChildAt(j);
+                ItemInfo info = (ItemInfo) v.getTag();
+                if (info instanceof FolderInfo && v instanceof FolderIcon) {
+                    FolderIcon folder = (FolderIcon) v;
+                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                    for (View fv : folderChildren) {
+                        info = (ItemInfo) fv.getTag();
+                        updateShortcut(appsMap, info, fv);
                     }
+                    folder.invalidate();
+                } else if (info instanceof ShortcutInfo) {
+                    updateShortcut(appsMap, info, v);
                 }
             }
         }