Load PackageItemInfo in background thread to prevent ANR

- Decoupled widget model from widget view, and placed the
creation to LauncherModel.
- As a result packagemanager operation, iconcache retrieval is all done inside
LauncherModel on background thread
b/21311085
b/21325319

Change-Id: I294698527db58b89f3da558090a367530c058776
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3b3b9bf..d530009 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -38,7 +38,7 @@
 import android.widget.TextView;
 
 import com.android.launcher3.IconCache.IconLoadRequest;
-import com.android.launcher3.widget.PackageItemInfo;
+import com.android.launcher3.model.PackageItemInfo;
 
 /**
  * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 1fac5a1..a387579 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -42,9 +42,9 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PackageItemInfo;
 
 import java.util.Collection;
 import java.util.HashMap;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7b7b617..59ca978 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -102,6 +102,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -268,8 +269,9 @@
     // Main container view for the all apps screen.
     @Thunk AppsContainerView mAppsView;
 
-    // Main container view for the widget tray screen.
-    private WidgetsContainerView mWidgetsView;
+    // Main container view and the model for the widget tray screen.
+    @Thunk WidgetsContainerView mWidgetsView;
+    @Thunk WidgetsModel mWidgetsModel;
 
     private boolean mAutoAdvanceRunning = false;
     private AppWidgetHostView mQsb;
@@ -4365,23 +4367,22 @@
         }
     }
 
-    @Thunk ArrayList<Object> mWidgetsAndShortcuts;
     private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
             public void run() {
-                bindAllPackages(mWidgetsAndShortcuts);
+                bindAllPackages(mWidgetsModel);
             }
         };
 
     @Override
-    public void bindAllPackages(final ArrayList<Object> widgetsAndShortcuts) {
+    public void bindAllPackages(final WidgetsModel model) {
         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
-            mWidgetsAndShortcuts = widgetsAndShortcuts;
+            mWidgetsModel = model;
             return;
         }
 
-        if (mWidgetsView != null && widgetsAndShortcuts != null) {
-            mWidgetsView.addWidgets(widgetsAndShortcuts, getPackageManager());
-            mWidgetsAndShortcuts = null;
+        if (mWidgetsView != null && model != null) {
+            mWidgetsView.addWidgets(model);
+            mWidgetsModel = null;
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 3e05f57..5002538 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -58,6 +58,7 @@
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
@@ -203,7 +204,7 @@
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
-        public void bindAllPackages(ArrayList<Object> widgetsAndShortcuts);
+        public void bindAllPackages(WidgetsModel model);
         public void bindSearchablesChanged();
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
@@ -3344,18 +3345,19 @@
         runOnWorkerThread(new Runnable(){
             @Override
             public void run() {
-                final ArrayList<Object> list = getWidgetsAndShortcuts(context, refresh);
+                final WidgetsModel model = createWidgetsModel(context, refresh);
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindAllPackages(list);
+                            callbacks.bindAllPackages(model);
                         }
                     }
                 });
                 // update the Widget entries inside DB on the worker thread.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(list);
+                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
+                        model.getRawList());
             }
         });
     }
@@ -3365,13 +3367,15 @@
      *
      *  @see #loadAndBindWidgetsAndShortcuts
      */
-    private ArrayList<Object> getWidgetsAndShortcuts(Context context, boolean refresh) {
+    private WidgetsModel createWidgetsModel(Context context, boolean refresh) {
         PackageManager packageManager = context.getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
         widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
         widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
-        return widgetsAndShortcuts;
+        WidgetsModel model = new WidgetsModel(context);
+        model.addWidgetsAndShortcuts(widgetsAndShortcuts);
+        return model;
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 6734fdc..1f8a6f2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -44,6 +44,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
+import android.os.Process;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -57,6 +58,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import junit.framework.Assert;
+
 /**
  * Various utilities shared amongst the Launcher's classes.
  */
@@ -641,4 +644,10 @@
         return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
                 (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
     }
+
+    public static void assertWorkerThread() {
+        if (LauncherAppState.isDogfoodBuild()) {
+            Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
+        }
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index bbcdaae..5ee1f26 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -206,8 +206,7 @@
      * This ensures that we remove entries for packages which changed while the launcher was dead.
      */
     public void removeObsoletePreviews(ArrayList<Object> list) {
-        // This method should always be called from the worker thread.
-        Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
+        Utilities.assertWorkerThread();
 
         LongSparseArray<UserHandleCompat> userIdCache = new LongSparseArray<>();
         LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
similarity index 97%
rename from src/com/android/launcher3/widget/PackageItemInfo.java
rename to src/com/android/launcher3/model/PackageItemInfo.java
index 8f45a77..0f0134a 100644
--- a/src/com/android/launcher3/widget/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.widget;
+package com.android.launcher3.model;
 
 import android.content.ComponentName;
 import android.graphics.Bitmap;
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
similarity index 81%
rename from src/com/android/launcher3/widget/WidgetsModel.java
rename to src/com/android/launcher3/model/WidgetsModel.java
index 5a920e8..b72b981 100644
--- a/src/com/android/launcher3/widget/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,18 +1,19 @@
 
-package com.android.launcher3.widget;
+package com.android.launcher3.model;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.support.v7.widget.RecyclerView;
+import android.os.Handler;
+import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.model.AppNameComparator;
-import com.android.launcher3.model.WidgetsAndShortcutNameComparator;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -37,19 +38,19 @@
     /* Map of widgets and shortcuts that are tracked per package. */
     private Map<PackageItemInfo, ArrayList<Object>> mWidgetsList = new HashMap<>();
 
-    /* Notifies the adapter when data changes. */
-    private RecyclerView.Adapter mAdapter;
+    private ArrayList<Object> mRawList;
 
-    private Comparator mWidgetAndShortcutNameComparator;
-    private Comparator mAppNameComparator;
+    private final Comparator mWidgetAndShortcutNameComparator;
+    private final Comparator mAppNameComparator;
 
-    private IconCache mIconCache;
+    private final IconCache mIconCache;
+    private final Handler mWorkerHandler;
 
-    public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
-        mAdapter = adapter;
+    public WidgetsModel(Context context) {
         mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
         mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
         mIconCache = LauncherAppState.getInstance().getIconCache();
+        mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
     }
 
     // Access methods that may be deleted if the private fields are made package-private.
@@ -66,9 +67,15 @@
         return mWidgetsList.get(mPackageItemInfos.get(pos));
     }
 
-    public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+    public ArrayList<Object> getRawList() {
+        return mRawList;
+    }
+
+    public void addWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+        Utilities.assertWorkerThread();
+        mRawList = rawWidgetsShortcuts;
         if (DEBUG) {
-            Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
+            Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
         }
 
         // Temporary list for {@link PackageItemInfos} to avoid having to go through
@@ -80,7 +87,7 @@
         mPackageItemInfos.clear();
 
         // add and update.
-        for (Object o: widgetsShortcuts) {
+        for (Object o: rawWidgetsShortcuts) {
             String packageName = "";
             if (o instanceof LauncherAppWidgetProviderInfo) {
                 LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
@@ -100,10 +107,9 @@
             } else {
                 widgetsShortcutsList = new ArrayList<Object>();
                 widgetsShortcutsList.add(o);
-
                 pInfo = new PackageItemInfo(packageName);
                 mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
-                        true /* useLowResIcon */, pInfo);
+                        true /* userLowResIcon */, pInfo);
                 mWidgetsList.put(pInfo, widgetsShortcutsList);
                 tmpPackageItemInfos.put(packageName,  pInfo);
                 mPackageItemInfos.add(pInfo);
@@ -115,8 +121,5 @@
         for (PackageItemInfo p: mPackageItemInfos) {
             Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
         }
-
-        // notify.
-        mAdapter.notifyDataSetChanged();
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 7a7895f..5a879fa 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -36,6 +35,7 @@
 import com.android.launcher3.DragController;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.Folder;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.ItemInfo;
@@ -47,8 +47,6 @@
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.Workspace;
 
-import java.util.ArrayList;
-
 /**
  * The widgets list view container.
  */
@@ -68,9 +66,6 @@
     private DragController mDragController;
     private IconCache mIconCache;
 
-    /* Data model for the widget */
-    private WidgetsModel mWidgets;
-
     /* Recycler view related member variables */
     private RecyclerView mView;
     private WidgetsListAdapter mAdapter;
@@ -98,8 +93,6 @@
         mDragController = mLauncher.getDragController();
         mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
         mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
-        mWidgets = new WidgetsModel(context, mAdapter);
-        mAdapter.setWidgetsModel(mWidgets);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
 
         if (DEBUG) {
@@ -109,10 +102,6 @@
 
     @Override
     protected void onFinishInflate() {
-        if (DEBUG) {
-            Log.d(TAG, String.format("onFinishInflate [widgets size=%d]",
-                    mWidgets.getPackageSize()));
-        }
         mView = (RecyclerView) findViewById(R.id.widgets_list_view);
         mView.setAdapter(mAdapter);
 
@@ -145,10 +134,6 @@
 
     public void scrollToTop() {
         mView.scrollToPosition(0);
-        if (DEBUG) {
-            Log.d(TAG, String.format("scrollToTop, [widgets size=%d]",
-                    mWidgets.getPackageSize()));
-        }
     }
 
     //
@@ -374,8 +359,9 @@
     /**
      * Initialize the widget data model.
      */
-    public void addWidgets(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
-        mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm);
+    public void addWidgets(WidgetsModel model) {
+        mAdapter.setWidgetsModel(model);
+        mAdapter.notifyDataSetChanged();
     }
 
     private WidgetPreviewLoader getWidgetPreviewLoader() {
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 3916c00..918ec1b 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -38,6 +38,8 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetsModel;
 
 import java.util.List;