Fixing slow binding when returning to Launcher

Bug: 8978842
Bug: 8660324

Change-Id: Idfa37c05ed299faa465ea66de4b43d30da77ecbc
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 901b661..95ce337 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -240,6 +240,9 @@
 
     WidgetPreviewLoader mWidgetPreviewLoader;
 
+    private boolean mInBulkBind;
+    private boolean mNeedToUpdatePageCountsAndInvalidateData;
+
     public AppsCustomizePagedView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLayoutInflater = LayoutInflater.from(context);
@@ -440,38 +443,57 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
-    public void onPackagesUpdated() {
+    public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
         // Get the list of widgets and shortcuts
         mWidgets.clear();
-        List<AppWidgetProviderInfo> widgets =
-            AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
-        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-        List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
-        for (AppWidgetProviderInfo widget : widgets) {
-            widget.label = widget.label.trim();
-            if (widget.minWidth > 0 && widget.minHeight > 0) {
-                // Ensure that all widgets we show can be added on a workspace of this size
-                int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
-                int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget);
-                int minSpanX = Math.min(spanXY[0], minSpanXY[0]);
-                int minSpanY = Math.min(spanXY[1], minSpanXY[1]);
-                if (minSpanX <= LauncherModel.getCellCountX() &&
+        for (Object o : widgetsAndShortcuts) {
+            if (o instanceof AppWidgetProviderInfo) {
+                AppWidgetProviderInfo widget = (AppWidgetProviderInfo) o;
+                widget.label = widget.label.trim();
+                if (widget.minWidth > 0 && widget.minHeight > 0) {
+                    // Ensure that all widgets we show can be added on a workspace of this size
+                    int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
+                    int[] minSpanXY = Launcher.getMinSpanForWidget(mLauncher, widget);
+                    int minSpanX = Math.min(spanXY[0], minSpanXY[0]);
+                    int minSpanY = Math.min(spanXY[1], minSpanXY[1]);
+                    if (minSpanX <= LauncherModel.getCellCountX() &&
                         minSpanY <= LauncherModel.getCellCountY()) {
-                    mWidgets.add(widget);
+                        mWidgets.add(widget);
+                    } else {
+                        Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" +
+                              widget.minWidth + ", " + widget.minHeight + ")");
+                    }
                 } else {
-                    Log.e(TAG, "Widget " + widget.provider + " can not fit on this device (" +
-                            widget.minWidth + ", " + widget.minHeight + ")");
+                    Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" +
+                          widget.minWidth + ", " + widget.minHeight + ")");
                 }
             } else {
-                Log.e(TAG, "Widget " + widget.provider + " has invalid dimensions (" +
-                        widget.minWidth + ", " + widget.minHeight + ")");
+                // just add shortcuts
+                mWidgets.add(o);
             }
         }
-        mWidgets.addAll(shortcuts);
-        Collections.sort(mWidgets,
-                new LauncherModel.WidgetAndShortcutNameComparator(mPackageManager));
-        updatePageCounts();
-        invalidateOnDataChange();
+        updatePageCountsAndInvalidateData();
+    }
+
+    public void setBulkBind(boolean bulkBind) {
+        if (bulkBind) {
+            mInBulkBind = true;
+        } else {
+            mInBulkBind = false;
+            if (mNeedToUpdatePageCountsAndInvalidateData) {
+                updatePageCountsAndInvalidateData();
+            }
+        }
+    }
+
+    private void updatePageCountsAndInvalidateData() {
+        if (mInBulkBind) {
+            mNeedToUpdatePageCountsAndInvalidateData = true;
+        } else {
+            updatePageCounts();
+            invalidateOnDataChange();
+            mNeedToUpdatePageCountsAndInvalidateData = false;
+        }
     }
 
     @Override
@@ -1526,8 +1548,7 @@
     public void setApps(ArrayList<ApplicationInfo> list) {
         mApps = list;
         Collections.sort(mApps, LauncherModel.getAppNameComparator());
-        updatePageCounts();
-        invalidateOnDataChange();
+        updatePageCountsAndInvalidateData();
     }
     private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
         // We add it in place, in alphabetical order
@@ -1542,8 +1563,7 @@
     }
     public void addApps(ArrayList<ApplicationInfo> list) {
         addAppsWithoutInvalidate(list);
-        updatePageCounts();
-        invalidateOnDataChange();
+        updatePageCountsAndInvalidateData();
     }
     private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
         ComponentName removeComponent = item.intent.getComponent();
@@ -1569,8 +1589,7 @@
     }
     public void removeApps(ArrayList<ApplicationInfo> appInfos) {
         removeAppsWithoutInvalidate(appInfos);
-        updatePageCounts();
-        invalidateOnDataChange();
+        updatePageCountsAndInvalidateData();
     }
     public void updateApps(ArrayList<ApplicationInfo> list) {
         // We remove and re-add the updated applications list because it's properties may have
@@ -1578,8 +1597,7 @@
         // place in the list.
         removeAppsWithoutInvalidate(list);
         addAppsWithoutInvalidate(list);
-        updatePageCounts();
-        invalidateOnDataChange();
+        updatePageCountsAndInvalidateData();
     }
 
     public void reset() {
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 473a3d1..94a0bca 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -126,7 +126,7 @@
     static final boolean PROFILE_STARTUP = false;
     static final boolean DEBUG_WIDGETS = false;
     static final boolean DEBUG_STRICT_MODE = false;
-    static final boolean DEBUG_RESUME_TIME = true;
+    static final boolean DEBUG_RESUME_TIME = false;
 
     private static final int MENU_GROUP_WALLPAPER = 1;
     private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1;
@@ -392,7 +392,8 @@
 
         // Update customization drawer _after_ restoring the states
         if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.onPackagesUpdated();
+            mAppsCustomizeContent.onPackagesUpdated(
+                LauncherModel.getSortedWidgetsAndShortcuts(this));
         }
 
         if (PROFILE_STARTUP) {
@@ -759,19 +760,28 @@
             mRestoring = false;
             mOnResumeNeedsLoad = false;
         }
-        // We might have postponed some bind calls until onResume (see waitUntilResume) --
-        // execute them here
-        long startTimeCallbacks = 0;
-        if (DEBUG_RESUME_TIME) {
-            startTimeCallbacks = System.currentTimeMillis();
-        }
-        for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
-            mOnResumeCallbacks.get(i).run();
-        }
-        mOnResumeCallbacks.clear();
-        if (DEBUG_RESUME_TIME) {
-            Log.d(TAG, "Time spent processing callbacks in onResume: " +
-                (System.currentTimeMillis() - startTimeCallbacks));
+        if (mOnResumeCallbacks.size() > 0) {
+            // We might have postponed some bind calls until onResume (see waitUntilResume) --
+            // execute them here
+            long startTimeCallbacks = 0;
+            if (DEBUG_RESUME_TIME) {
+                startTimeCallbacks = System.currentTimeMillis();
+            }
+
+            if (mAppsCustomizeContent != null) {
+                mAppsCustomizeContent.setBulkBind(true);
+            }
+            for (int i = 0; i < mOnResumeCallbacks.size(); i++) {
+                mOnResumeCallbacks.get(i).run();
+            }
+            if (mAppsCustomizeContent != null) {
+                mAppsCustomizeContent.setBulkBind(false);
+            }
+            mOnResumeCallbacks.clear();
+            if (DEBUG_RESUME_TIME) {
+                Log.d(TAG, "Time spent processing callbacks in onResume: " +
+                    (System.currentTimeMillis() - startTimeCallbacks));
+            }
         }
 
         // Reset the pressed state of icons that were locked in the press state while activities
@@ -3308,9 +3318,13 @@
      * @return true if we are currently paused.  The caller might be able to
      * skip some work in that case since we will come back again.
      */
-    private boolean waitUntilResume(Runnable run) {
+    private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
         if (mPaused) {
             Log.i(TAG, "Deferring update until onResume");
+            if (deletePreviousRunnables) {
+                while (mOnResumeCallbacks.remove(run)) {
+                }
+            }
             mOnResumeCallbacks.add(run);
             return true;
         } else {
@@ -3318,6 +3332,10 @@
         }
     }
 
+    private boolean waitUntilResume(Runnable run) {
+        return waitUntilResume(run, false);
+    }
+
     /**
      * If the activity is currently paused, signal that we need to re-run the loader
      * in onResume.
@@ -3761,17 +3779,23 @@
     /**
      * A number of packages were updated.
      */
-    public void bindPackagesUpdated() {
-        if (waitUntilResume(new Runnable() {
+
+    private ArrayList<Object> mWidgetsAndShortcuts;
+    private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
             public void run() {
-                bindPackagesUpdated();
+                bindPackagesUpdated(mWidgetsAndShortcuts);
+                mWidgetsAndShortcuts = null;
             }
-        })) {
+        };
+
+    public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
+        if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
+            mWidgetsAndShortcuts = widgetsAndShortcuts;
             return;
         }
 
         if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.onPackagesUpdated();
+            mAppsCustomizeContent.onPackagesUpdated(widgetsAndShortcuts);
         }
     }
 
diff --git a/src/com/android/launcher2/LauncherAppWidgetHost.java b/src/com/android/launcher2/LauncherAppWidgetHost.java
index 45c1d23..4d52ea8 100644
--- a/src/com/android/launcher2/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher2/LauncherAppWidgetHost.java
@@ -50,6 +50,6 @@
     protected void onProvidersChanged() {
         // Once we get the message that widget packages are updated, we need to rebind items
         // in AppsCustomize accordingly.
-        mLauncher.bindPackagesUpdated();
+        mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher));
     }
 }
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 99ebd96..1d562be 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -161,7 +161,7 @@
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<ApplicationInfo> appInfos,
                         boolean matchPackageNamesOnly);
-        public void bindPackagesUpdated();
+        public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public boolean isAllAppsVisible();
         public boolean isAllAppsButtonRank(int rank);
         public void bindSearchablesChanged();
@@ -2105,18 +2105,32 @@
                 });
             }
 
+            final ArrayList<Object> widgetsAndShortcuts =
+                getSortedWidgetsAndShortcuts(context);
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
                     if (callbacks == cb && cb != null) {
-                        callbacks.bindPackagesUpdated();
+                        callbacks.bindPackagesUpdated(widgetsAndShortcuts);
                     }
                 }
             });
         }
     }
 
+    // Returns a list of ResolveInfos/AppWindowInfos in sorted order
+    public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
+        widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
+        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+        widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
+        Collections.sort(widgetsAndShortcuts,
+            new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
+        return widgetsAndShortcuts;
+    }
+
     /**
      * This is called from the code that adds shortcuts from the intent receiver.  This
      * doesn't have a Cursor, but