Merge "Fix widget tray clipping due to wrong padding" into ub-launcher3-calgary
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index f5fcf47..e1c52b2 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -800,7 +800,7 @@
     }
 
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 7;
+        private final static int DB_VERSION = 8;
 
         private final static int RELEASE_VERSION = DB_VERSION +
                 (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -859,6 +859,7 @@
             Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
                     icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
             synchronized (this) {
+                mLowResCanvas.setBitmap(lowResIcon);
                 mLowResCanvas.drawColor(lowResBackgroundColor);
                 mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
                         new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fc828da..c8eb9cc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,7 +82,6 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
@@ -109,8 +108,10 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.UserEventLogger;
 import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.userevent.Logger;
+import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.TestingUtils;
@@ -131,6 +132,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Default launcher application.
@@ -366,7 +368,7 @@
     }
 
     private Stats mStats;
-    private Logger mUserEventLogger;
+    private UserEventLogger mUserEventLogger;
 
     public FocusIndicatorView mFocusHandler;
     private boolean mRotationEnabled = false;
@@ -425,8 +427,8 @@
         mDragController = new DragController(this);
         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
 
-        mUserEventLogger = new Logger(this);
         mStats = new Stats(this);
+        initLogger();
 
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
@@ -536,7 +538,7 @@
             private boolean mWorkspaceImportanceStored = false;
             private boolean mHotseatImportanceStored = false;
             private int mWorkspaceImportanceForAccessibility =
-                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+                    View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
             private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 
             @Override
@@ -632,7 +634,35 @@
     public Stats getStats() {
         return mStats;
     }
-    public Logger getLogger() {return mUserEventLogger; }
+
+    /**
+     * Logger object is a singleton and does not have to be coupled with the foreground activity.
+     * Since most user event logging is done on the UI, the object is retrieved from the
+     * callback for convenience.
+     */
+    private void initLogger() {
+        if (mLauncherCallbacks != null) {
+            mUserEventLogger = mLauncherCallbacks.getLogger();
+        }
+        if (mUserEventLogger == null) {
+            mUserEventLogger = new UserEventLogger() {
+                @Override
+                public void processEvent(LauncherLogProto.LauncherEvent ev) {
+                    if (ev.action.touch == LauncherLogProto.Action.TAP && ev.srcTarget.itemType == LauncherLogProto.APP_ICON) {
+                        Log.d(TAG, String.format(Locale.US, "action:%s target:%s\n\telapsed container %d ms session %d ms",
+                                LoggerUtils.getActionStr(ev.action),
+                                LoggerUtils.getTargetStr(ev.srcTarget),
+                                ev.elapsedContainerMillis,
+                                ev.elapsedSessionMillis));
+                    }
+                }
+            };
+        }
+    }
+
+    public UserEventLogger getLogger() {
+        return mUserEventLogger;
+    }
 
     public boolean isDraggingEnabled() {
         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
@@ -1530,8 +1560,7 @@
 
         ItemInfo info = mPendingAddInfo;
         if (appWidgetInfo == null) {
-            appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
-                    mAppWidgetManager.getAppWidgetInfo(appWidgetId));
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
         }
 
         if (appWidgetInfo.isCustomWidget) {
@@ -2528,10 +2557,10 @@
         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
         if (v.isReadyForClickSetup()) {
             int widgetId = info.appWidgetId;
-            AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+            LauncherAppWidgetProviderInfo appWidgetInfo =
+                    mAppWidgetManager.getLauncherAppWidgetInfo(widgetId);
             if (appWidgetInfo != null) {
-                mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
-                        this, appWidgetInfo);
+                mPendingAddWidgetInfo = appWidgetInfo;
                 mPendingAddInfo.copyFrom(info);
                 mPendingAddWidgetId = widgetId;
 
@@ -3302,7 +3331,6 @@
             getWindow().getDecorView()
                     .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
-        mUserEventLogger.resetElapsedContainerMillis();
         return changed;
     }
 
@@ -3509,10 +3537,6 @@
         // TODO
     }
 
-    protected void disableVoiceButtonProxy(boolean disable) {
-        // NO-OP
-    }
-
     public boolean launcherCallbacksProvidesSearch() {
         return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
     }
@@ -3926,6 +3950,16 @@
         sFolders = folders.clone();
     }
 
+    private void bindSafeModeWidget(LauncherAppWidgetInfo item) {
+        PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, true);
+        view.updateIcon(mIconCache);
+        item.hostView = view;
+        item.hostView.updateAppWidget(null);
+        item.hostView.setOnClickListener(this);
+        addAppWidgetToWorkspace(item, null, false);
+        mWorkspace.requestLayout();
+    }
+
     /**
      * Add the views for a widget to the workspace.
      *
@@ -3941,19 +3975,31 @@
             return;
         }
 
+        if (mIsSafeModeEnabled) {
+            bindSafeModeWidget(item);
+            return;
+        }
+
         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bindAppWidget: " + item);
         }
-        final Workspace workspace = mWorkspace;
 
-        LauncherAppWidgetProviderInfo appWidgetInfo =
-                LauncherModel.getProviderInfo(this, item.providerName, item.user);
+        final LauncherAppWidgetProviderInfo appWidgetInfo;
 
-        if (!mIsSafeModeEnabled
-                && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
-                && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+        if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+            // If the provider is not ready, bind as a pending widget.
+            appWidgetInfo = null;
+        } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+            // The widget id is not valid. Try to find the widget based on the provider info.
+            appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+        } else {
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+        }
 
+        // If the provider is ready, but the width is not yet restored, try to restore it.
+        if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
+                (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
             if (appWidgetInfo == null) {
                 if (DEBUG_WIDGETS) {
                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -3965,7 +4011,7 @@
             }
 
             // If we do not have a valid id, try to bind an id.
-            if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
                 // Note: This assumes that the id remap broadcast is received before this step.
                 // If that is not the case, the id remap will be ignored and user may see the
                 // click to setup view.
@@ -4001,46 +4047,42 @@
                         : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
 
                 LauncherModel.updateItemInDatabase(this, item);
-            } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
                     && (appWidgetInfo.configure == null)) {
-                // If the ID is already valid, verify if we need to configure or not.
+                // The widget was marked as UI not ready, but there is no configure activity to
+                // update the UI.
                 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                 LauncherModel.updateItemInDatabase(this, item);
             }
         }
 
-        if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-            final int appWidgetId = item.appWidgetId;
+        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
             if (DEBUG_WIDGETS) {
                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
                         + appWidgetInfo.provider);
             }
 
             // Verify that we own the widget
-            AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-            if (info == null || appWidgetInfo == null ||
-                    !info.provider.equals(appWidgetInfo.provider)) {
-                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId + " info=" + info
-                        + " appWidgetInfo=" + appWidgetInfo);
+            if (appWidgetInfo == null) {
+                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                 deleteWidgetInfo(item);
                 return;
             }
 
-            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            item.hostView = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             item.minSpanX = appWidgetInfo.minSpanX;
             item.minSpanY = appWidgetInfo.minSpanY;
+            addAppWidgetToWorkspace(item, appWidgetInfo, false);
         } else {
-            appWidgetInfo = null;
             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
                     mIsSafeModeEnabled);
             view.updateIcon(mIconCache);
             item.hostView = view;
             item.hostView.updateAppWidget(null);
             item.hostView.setOnClickListener(this);
+            addAppWidgetToWorkspace(item, null, false);
         }
-
-        addAppWidgetToWorkspace(item, appWidgetInfo, false);
-        workspace.requestLayout();
+        mWorkspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
@@ -4378,15 +4420,15 @@
         }
     }
 
-    private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
+    private Runnable mBindWidgetModelRunnable = new Runnable() {
             public void run() {
-                bindAllPackages(mWidgetsModel);
+                bindWidgetsModel(mWidgetsModel);
             }
         };
 
     @Override
-    public void bindAllPackages(final WidgetsModel model) {
-        if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
+    public void bindWidgetsModel(WidgetsModel model) {
+        if (waitUntilResume(mBindWidgetModelRunnable, true)) {
             mWidgetsModel = model;
             return;
         }
@@ -4397,6 +4439,13 @@
         }
     }
 
+    @Override
+    public void notifyWidgetProvidersChanged() {
+        if (mWorkspace.getState().shouldUpdateWidget) {
+            mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
+        }
+    }
+
     private int mapConfigurationOriActivityInfoOri(int configOri) {
         final Display d = getWindowManager().getDefaultDisplay();
         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index b07ccc3..8c23ff3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -102,6 +102,10 @@
                 callback.run();
             }
         }
+
+        if (Utilities.ATLEAST_MARSHMALLOW) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
     }
 
     public AppWidgetHostView createView(Context context, int appWidgetId,
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 05d729e..cad0f2c 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.backup.nano.BackupProtos.Resource;
 import com.android.launcher3.backup.nano.BackupProtos.Screen;
 import com.android.launcher3.backup.nano.BackupProtos.Widget;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.model.GridSizeMigrationTask;
@@ -660,12 +661,14 @@
                 + getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
+        AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
                 final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX);
                 final ComponentName provider = ComponentName.unflattenFromString(providerName);
+
                 Key key = null;
                 String backupKey = null;
                 if (provider != null) {
@@ -684,11 +687,14 @@
                 } else if (backupKey != null) {
                     if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
                     if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
-                        if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
-                        UserHandleCompat user = UserHandleCompat.myUserHandle();
-                        writeRowToBackup(key, packWidget(dpi, provider, user), data);
-                        mKeys.add(key);
-                        backupWidgetCount ++;
+                        LauncherAppWidgetProviderInfo widgetInfo = widgetManager
+                                .getLauncherAppWidgetInfo(cursor.getInt(APPWIDGET_ID_INDEX));
+                        if (widgetInfo != null) {
+                            if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
+                            writeRowToBackup(key, packWidget(dpi, widgetInfo), data);
+                            mKeys.add(key);
+                            backupWidgetCount ++;
+                        }
                     } else {
                         if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey);
                         // too many widgets for this pass, request another.
@@ -1004,16 +1010,14 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) {
-        final LauncherAppWidgetProviderInfo info =
-                LauncherModel.getProviderInfo(mContext, provider, user);
+    private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) {
         Widget widget = new Widget();
-        widget.provider = provider.flattenToShortString();
+        widget.provider = info.provider.flattenToShortString();
         widget.label = info.label;
         widget.configure = info.configure != null;
         if (info.icon != 0) {
             widget.icon = new Resource();
-            Drawable fullResIcon = mIconCache.getFullResIcon(provider.getPackageName(), info.icon);
+            Drawable fullResIcon = mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
             Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
             widget.icon.data = Utilities.flattenBitmap(icon);
             widget.icon.dpi = dpi;
@@ -1022,7 +1026,6 @@
         Point spans = info.getMinSpans(mIdp, mContext);
         widget.minSpanX = spans.x;
         widget.minSpanY = spans.y;
-
         return widget;
     }
 
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 0be45c3..fc7ff70 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -8,6 +8,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import com.android.launcher3.allapps.AllAppsSearchBarController;
+import com.android.launcher3.logging.UserEventLogger;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -97,6 +98,7 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
+    public UserEventLogger getLogger();
     public Intent getFirstRunActivity();
     public boolean hasFirstRunActivity();
     public boolean hasDismissableIntroScreen();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f707ec5..3877b94 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.app.SearchManager;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -34,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.DeadObjectException;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -42,7 +40,6 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.TransactionTooLargeException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
@@ -73,7 +70,6 @@
 import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -137,11 +133,9 @@
     @Thunk WeakReference<Callbacks> mCallbacks;
 
     // < only access in worker thread >
-    AllAppsList mBgAllAppsList;
+    private final AllAppsList mBgAllAppsList;
     // Entire list of widgets.
-    WidgetsModel mBgWidgetsModel;
-    // Keep a clone of widgets that can be accessed from non-worker thread.
-    WidgetsModel mFgWidgetsModel;
+    private final WidgetsModel mBgWidgetsModel;
 
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
@@ -168,12 +162,6 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
-    // sBgWidgetProviders is the set of widget providers including custom internal widgets
-    public static HashMap<ComponentKey, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
-
-    // sBgShortcutProviders is the set of custom shortcut providers
-    public static List<ResolveInfo> sBgShortcutProviders;
-
     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
             new HashMap<UserHandleCompat, HashSet<String>>();
@@ -209,7 +197,8 @@
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
                         ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
-        public void bindAllPackages(WidgetsModel model);
+        public void notifyWidgetProvidersChanged();
+        public void bindWidgetsModel(WidgetsModel model);
         public void bindSearchProviderChanged();
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
@@ -245,7 +234,6 @@
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
         mIconCache = iconCache;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
@@ -1701,8 +1689,8 @@
                         .getInstance(mContext).updateAndGetActiveSessionCache();
                 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
 
-                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
-                final ArrayList<Long> restoredRows = new ArrayList<Long>();
+                final ArrayList<Long> itemsToRemove = new ArrayList<>();
+                final ArrayList<Long> restoredRows = new ArrayList<>();
                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1711,6 +1699,7 @@
                 // Load workspace in reverse order to ensure that latest items are loaded first (and
                 // before any earlier duplicates)
                 final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
+                HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
 
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -2070,10 +2059,14 @@
                                 final boolean wasProviderReady = (restoreStatus &
                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
 
-                                final LauncherAppWidgetProviderInfo provider =
-                                        LauncherModel.getProviderInfo(context,
+                                if (widgetProvidersMap == null) {
+                                    widgetProvidersMap = AppWidgetManagerCompat
+                                            .getInstance(mContext).getAllProvidersMap();
+                                }
+                                final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+                                        new ComponentKey(
                                                 ComponentName.unflattenFromString(savedProvider),
-                                                user);
+                                                user));
 
                                 final boolean isProviderReady = isValidProvider(provider);
                                 if (!isSafeMode && !customWidget &&
@@ -2695,7 +2688,6 @@
                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(list);
-                        callbacks.bindAllPackages(mFgWidgetsModel);
                     }
                     if (DEBUG_LOADERS) {
                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
@@ -2787,7 +2779,7 @@
                         callbacks.bindAllApplications(added);
                         if (DEBUG_LOADERS) {
                             Log.d(TAG, "bound " + added.size() + " apps in "
-                                + (SystemClock.uptimeMillis() - bindTime) + "ms");
+                                    + (SystemClock.uptimeMillis() - bindTime) + "ms");
                         }
                     } else {
                         Log.i(TAG, "not binding apps: no Launcher activity");
@@ -2796,8 +2788,6 @@
             });
             // Cleanup any data stored for a deleted user.
             ManagedProfileHeuristic.processAllUsers(profiles, mContext);
-
-            loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
             if (DEBUG_LOADERS) {
                 Log.d(TAG, "Icons processed in "
                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2864,9 +2854,6 @@
                 }
             });
         }
-
-        // Reload widget list. No need to refresh, as we only want to update the icons and labels.
-        loadAndBindWidgetsAndShortcuts(callbacks, false);
     }
 
     void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -3230,167 +3217,49 @@
                 });
             }
 
-            // Update widgets
-            if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) {
-                // Always refresh for a package event on secondary user
-                boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle());
-
-                // Refresh widget list, if the package already had a widget.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        HashSet<String> pkgSet = new HashSet<>();
-                        Collections.addAll(pkgSet, mPackages);
-
-                        for (ComponentKey key : sBgWidgetProviders.keySet()) {
-                            needToRefresh |= key.user.equals(mUser) &&
-                                    pkgSet.contains(key.componentName.getPackageName());
-                        }
-                    }
-                }
-
-                if (!needToRefresh && mOp != OP_REMOVE) {
-                    // Refresh widget list, if there is any newly added widget
-                    PackageManager pm = context.getPackageManager();
-                    for (String pkg : mPackages) {
-                        try {
-                            List<ResolveInfo> widgets = pm.queryBroadcastReceivers(
-                                    new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
-                                            .setPackage(pkg), 0);
-                            needToRefresh |= widgets != null && !widgets.isEmpty();
-                        } catch (RuntimeException e) {
-                            if (ProviderConfig.IS_DOGFOOD_BUILD) {
-                                throw e;
-                            }
-                            // Ignore the crash. We can live with a state widget list.
-                            Log.e(TAG, "PM call failed for " + pkg, e);
-                        }
-                    }
-                }
-
-                loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
-            }
-        }
-    }
-
-    public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
-            boolean refresh) {
-        ArrayList<LauncherAppWidgetProviderInfo> results =
-                new ArrayList<LauncherAppWidgetProviderInfo>();
-        try {
-            synchronized (sBgLock) {
-                if (sBgWidgetProviders == null || refresh) {
-                    HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
-                            = new HashMap<>();
-                    AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
-                    LauncherAppWidgetProviderInfo info;
-
-                    List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
-                    for (AppWidgetProviderInfo pInfo : widgets) {
-                        info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-
-                    Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
-                    for (CustomAppWidget widget : customWidgets) {
-                        info = new LauncherAppWidgetProviderInfo(context, widget);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-                    // Replace the global list at the very end, so that if there is an exception,
-                    // previously loaded provider list is used.
-                    sBgWidgetProviders = tmpWidgetProviders;
-                }
-                results.addAll(sBgWidgetProviders.values());
-                return results;
-            }
-        } catch (Exception e) {
-            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
-                    (e.getCause() instanceof TransactionTooLargeException ||
-                    e.getCause() instanceof DeadObjectException)) {
-                // the returned value may be incomplete and will not be refreshed until the next
-                // time Launcher starts.
-                // TODO: after figuring out a repro step, introduce a dirty bit to check when
-                // onResume is called to refresh the widget provider list.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        results.addAll(sBgWidgetProviders.values());
-                    }
-                    return results;
-                }
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name,
-            UserHandleCompat user) {
-        synchronized (sBgLock) {
-            if (sBgWidgetProviders == null) {
-                getWidgetProviders(ctx, false /* refresh */);
-            }
-            return sBgWidgetProviders.get(new ComponentKey(name, user));
-        }
-    }
-
-    public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
-
-        runOnWorkerThread(new Runnable() {
-            @Override
-            public void run() {
-                updateWidgetsModel(refresh);
+            // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
+            // get widget update signals.
+            if (!Utilities.ATLEAST_MARSHMALLOW &&
+                    (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
                 mHandler.post(new Runnable() {
-                    @Override
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindAllPackages(mFgWidgetsModel);
+                            callbacks.notifyWidgetProvidersChanged();
                         }
                     }
                 });
-                // update the Widget entries inside DB on the worker thread.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
-                        mFgWidgetsModel.getRawList());
+            }
+        }
+    }
+
+    private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                Callbacks cb = getCallback();
+                if (callbacks == cb && cb != null) {
+                    callbacks.bindWidgetsModel(model);
+                }
             }
         });
     }
 
-    /**
-     * Returns a list of ResolveInfos/AppWidgetInfos.
-     *
-     * @see #loadAndBindWidgetsAndShortcuts
-     */
-    @Thunk void updateWidgetsModel(boolean refresh) {
-        Utilities.assertWorkerThread();
-        PackageManager packageManager = mApp.getContext().getPackageManager();
-        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
-        widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
-
-        // Update shortcut providers
-        synchronized (sBgLock) {
-            try {
-                Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                List<ResolveInfo> providers = packageManager.queryIntentActivities(shortcutsIntent, 0);
-                sBgShortcutProviders = providers;
-            } catch (RuntimeException e) {
-                if (!ProviderConfig.IS_DOGFOOD_BUILD &&
-                        (e.getCause() instanceof TransactionTooLargeException ||
-                                e.getCause() instanceof DeadObjectException)) {
-                    /**
-                     * Ignore exception and use the cached list if available.
-                     * Refer to {@link #getWidgetProviders(Context, boolean}} for more info.
-                     */
-                } else {
-                    throw e;
+    public void refreshAndBindWidgetsAndShortcuts(
+            final Callbacks callbacks, final boolean bindFirst) {
+        runOnWorkerThread(new Runnable() {
+            @Override
+            public void run() {
+                if (bindFirst && !mBgWidgetsModel.isEmpty()) {
+                    bindWidgetsModel(callbacks, mBgWidgetsModel.clone());
                 }
+                final WidgetsModel model = mBgWidgetsModel.updateAndClone(mApp.getContext());
+                bindWidgetsModel(callbacks, model);
+                // update the Widget entries inside DB on the worker thread.
+                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
+                        model.getRawList());
             }
-            if (sBgShortcutProviders != null) {
-                widgetsAndShortcuts.addAll(sBgShortcutProviders);
-            }
-        }
-        mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
+        });
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 54945be..8855cf0 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -148,6 +148,7 @@
             }
             @Override
             void onTransitionComplete() {
+                mLauncher.getLogger().resetElapsedContainerMillis();
                 if (startSearchAfterTransition) {
                     toView.startAppsSearch();
                 }
@@ -167,7 +168,12 @@
         final View buttonView = mLauncher.getWidgetsButton();
         mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
                 Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
-                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS));
+                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
+                    @Override
+                    void onTransitionComplete() {
+                        mLauncher.getLogger().resetElapsedContainerMillis();
+                    }
+                });
     }
 
     /**
@@ -397,7 +403,7 @@
 
         // Animate the search bar
         final SearchDropTargetBar.State toSearchBarState =
-                toWorkspaceState.getSearchDropTargetBarState();
+                toWorkspaceState.searchDropTargetBarState;
         mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
                 animated ? revealDuration : 0, animation);
 
@@ -462,6 +468,10 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                mLauncher.getLogger().resetElapsedContainerMillis();
+            }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
@@ -488,6 +498,10 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                mLauncher.getLogger().resetElapsedContainerMillis();
+            }
         };
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
                 fromWorkspaceState, toWorkspaceState,
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index 2873828..10a26ad 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -26,7 +26,6 @@
 import android.view.ViewParent;
 
 import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.userevent.Logger;
 
 public class Stats {
 
@@ -146,6 +145,8 @@
         LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras);
         broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras);
         mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
-        mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras);
+        if (intent.getComponent() != null) {
+            mLauncher.getLogger().logAppLaunch(intent.getComponent().getPackageName(), shortcut, sourceExtras);
+        }
     }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a74fa0d..0b72ef7 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,11 +52,11 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
@@ -179,22 +179,20 @@
     // in all apps or customize mode)
 
     enum State {
-        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED),
-        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET),
-        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE),
-        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
+        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR, false),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false),
+        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET, false),
+        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE, true),
+        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true);
 
-        private final SearchDropTargetBar.State mBarState;
+        public final SearchDropTargetBar.State searchDropTargetBarState;
+        public final boolean shouldUpdateWidget;
 
-        State(SearchDropTargetBar.State searchBarState) {
-            mBarState = searchBarState;
+        State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget) {
+            searchDropTargetBarState = searchBarState;
+            this.shouldUpdateWidget = shouldUpdateWidget;
         }
-
-        public SearchDropTargetBar.State getSearchDropTargetBarState() {
-            return mBarState;
-        }
-    };
+    }
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.NORMAL;
@@ -1914,6 +1912,8 @@
         Animator workspaceAnim =  mStateTransitionAnimation.getAnimationToState(mState,
                 toState, animated, layerViews);
 
+        boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
+                && toState.shouldUpdateWidget;
         // Update the current state
         mState = toState;
         updateAccessibilityFlags();
@@ -1923,6 +1923,10 @@
             invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
         }
 
+        if (shouldNotifyWidgetChange) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
+
         return workspaceAnim;
     }
 
@@ -4148,13 +4152,22 @@
         });
     }
 
-    void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
+    public void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
                     mLauncher.getAppWidgetHost());
-            if (LauncherModel.getProviderInfo(getContext(),
-                    changedInfo.get(0).providerName,
-                    changedInfo.get(0).user) != null) {
+
+            LauncherAppWidgetInfo item = changedInfo.get(0);
+            final AppWidgetProviderInfo widgetInfo;
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                widgetInfo = AppWidgetManagerCompat
+                        .getInstance(mLauncher).findProvider(item.providerName, item.user);
+            } else {
+                widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
+                        .getAppWidgetInfo(item.appWidgetId);
+            }
+
+            if (widgetInfo != null) {
                 // Re-inflate the widgets which have changed status
                 widgetRefresh.run();
             } else {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 07bbc46..9d5afb4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -47,7 +47,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.userevent.Logger;
 import com.android.launcher3.util.ComponentKey;
 
 import java.nio.charset.Charset;
@@ -313,7 +312,6 @@
                 icon.getMeasuredHeight());
 
         updateBackgroundAndPaddings();
-        mLauncher.getLogger().resetElapsedContainerMillis();
     }
 
     @Override
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index f0221bc..811cacf 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -28,7 +29,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 public abstract class AppWidgetManagerCompat {
@@ -62,6 +65,11 @@
         return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
     }
 
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
+        AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId);
+        return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+    }
+
     public abstract List<AppWidgetProviderInfo> getAllProviders();
 
     public abstract String loadLabel(LauncherAppWidgetProviderInfo info);
@@ -81,4 +89,8 @@
     public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
             int imageWidth, int imageHeight);
 
+    public abstract LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user);
+
+    public abstract HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap();
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index e9d2510..de9414e 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -31,7 +32,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
@@ -91,4 +94,25 @@
             int imageWidth, int imageHeight) {
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        UserHandleCompat user = UserHandleCompat.myUserHandle();
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            result.put(new ComponentKey(info.provider, user), info);
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 3bc3d0d..a1570e6 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -40,8 +41,10 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.util.ComponentKey;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -145,4 +148,28 @@
         c.setBitmap(null);
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager
+                .getInstalledProvidersForProfile(user.getUser())) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        for (UserHandle user : mUserManager.getUserProfiles()) {
+            UserHandleCompat userHandle = UserHandleCompat.fromUser(user);
+            for (AppWidgetProviderInfo info :
+                    mAppWidgetManager.getInstalledProvidersForProfile(user)) {
+                result.put(new ComponentKey(info.provider, userHandle), info);
+            }
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
new file mode 100644
index 0000000..4b30384
--- /dev/null
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -0,0 +1,117 @@
+package com.android.launcher3.logging;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Stats;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+
+/**
+ * Debugging helper methods.
+ * toString() cannot be overriden inside auto generated {@link LauncherLogProto}.
+ * Note: switch statement cannot be replaced with reflection as proguard strips the constants
+ */
+public class LoggerUtils {
+    private static final String TAG = "LoggerUtils";
+    private static final boolean DEBUG = false;
+
+    static int getContainerType(ShortcutInfo shortcut) {
+        switch ((int) shortcut.container) {
+            case LauncherSettings.Favorites.CONTAINER_DESKTOP: return LauncherLogProto.WORKSPACE;
+            case LauncherSettings.Favorites.CONTAINER_HOTSEAT: return LauncherLogProto.HOTSEAT;
+            default:
+                return (int) shortcut.container;
+        }
+    }
+
+    public static String getActionStr(LauncherLogProto.Action action) {
+        switch(action.touch) {
+            case Action.TAP: return "TAP";
+            case Action.LONGPRESS: return "LONGPRESS";
+            case Action.DRAGDROP: return "DRAGDROP";
+            case Action.PINCH: return "PINCH";
+            default: return "UNKNOWN";
+        }
+    }
+
+    public static String getTargetStr(Target t) {
+        String typeStr;
+        switch (t.type) {
+            case Target.ITEM:
+                return getItemStr(t);
+            case Target.CONTROL:
+                return getControlStr(t);
+            case Target.CONTAINER:
+                return getContainerStr(t);
+            default:
+                return "UNKNOWN TARGET TYPE";
+        }
+    }
+
+    private static String getItemStr(Target t) {
+        String typeStr = "";
+        switch(t.itemType){
+            case LauncherLogProto.APP_ICON: typeStr = "ICON"; break;
+            case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
+            case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break;
+            default: typeStr = "UNKNOWN";
+        }
+
+        return typeStr + " " + t.packageNameHash + " grid=(" + t.gridX + "," + t.gridY + ") "
+                + getContainerStr(t.parent);
+    }
+
+    private static String getControlStr(Target t) {
+        switch(t.controlType) {
+            case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON";
+            case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON";
+            case LauncherLogProto.WALLPAPER_BUTTON: return "WALLPAPER_BUTTON";
+            case LauncherLogProto.SETTINGS_BUTTON: return "SETTINGS_BUTTON";
+            case LauncherLogProto.REMOVE_TARGET: return "REMOVE_TARGET";
+            case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET";
+            case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET";
+            case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE";
+            case LauncherLogProto.FAST_SCROLL_HANDLE: return "FAST_SCROLL_HANDLE";
+            default: return "UNKNOWN";
+        }
+    }
+
+    private static String getContainerStr(LauncherLogProto.Target t) {
+        String str;
+        Log.d(TAG, "t.containerType" + t.containerType);
+        switch (t.containerType) {
+            case LauncherLogProto.WORKSPACE:
+                str = "WORKSPACE";
+                break;
+            case LauncherLogProto.HOTSEAT:
+                str = "HOTSEAT";
+                break;
+            case LauncherLogProto.FOLDER:
+                str = "FOLDER";
+                break;
+            case LauncherLogProto.ALLAPPS:
+                str = "ALLAPPS";
+                break;
+            case LauncherLogProto.WIDGETS:
+                str = "WIDGETS";
+                break;
+            case LauncherLogProto.OVERVIEW:
+                str = "OVERVIEW";
+                break;
+            case LauncherLogProto.PREDICTION:
+                str = "PREDICTION";
+                break;
+            case LauncherLogProto.SEARCHRESULT:
+                str = "SEARCHRESULT";
+                break;
+            default:
+                str = "UNKNOWN";
+        }
+        return str + " id=" + t.pageIndex;
+    }
+}
diff --git a/src/com/android/launcher3/logging/UserEventLogger.java b/src/com/android/launcher3/logging/UserEventLogger.java
index d05b683..4e5b2c1 100644
--- a/src/com/android/launcher3/logging/UserEventLogger.java
+++ b/src/com/android/launcher3/logging/UserEventLogger.java
@@ -1,4 +1,97 @@
 package com.android.launcher3.logging;
 
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Stats;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+
+import java.util.Locale;
+
 public abstract class UserEventLogger {
+
+    private String TAG = "UserEventLogger";
+    private boolean DEBUG = false;
+
+    private long mElapsedContainerMillis;
+    private long mElapsedSessionMillis;
+    private long mActionDurationMillis;
+
+
+    public final void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) {
+        if (FeatureFlags.LAUNCHER3_LEGACY_LOGGING) return;
+
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+        event.action = new LauncherLogProto.Action();
+        event.action.type = LauncherLogProto.Action.TOUCH;
+        event.action.touch = LauncherLogProto.Action.TAP;
+
+        event.srcTarget = new LauncherLogProto.Target();
+        event.srcTarget.type = LauncherLogProto.Target.ITEM;
+        event.srcTarget.itemType = LauncherLogProto.APP_ICON;
+        // TODO: package hash name should be different per device.
+        event.srcTarget.packageNameHash = provider.hashCode();
+
+        event.srcTarget.parent = new LauncherLogProto.Target();
+        String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER);
+
+        if (shortcut != null) {
+            event.srcTarget.parent.containerType = LoggerUtils.getContainerType(shortcut);
+            event.srcTarget.pageIndex = (int) shortcut.screenId;
+            event.srcTarget.gridX = shortcut.cellX;
+            event.srcTarget.gridX = shortcut.cellY;
+        }
+        if (subContainer != null) {
+            event.srcTarget.parent.type = LauncherLogProto.Target.CONTAINER;
+            if (subContainer.equals(Stats.SUB_CONTAINER_FOLDER)) {
+                event.srcTarget.parent.containerType = LauncherLogProto.FOLDER;
+            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_A_Z)) {
+                event.srcTarget.parent.containerType = LauncherLogProto.ALLAPPS;
+            } else if (subContainer.equals(Stats.CONTAINER_HOTSEAT)) {
+                event.srcTarget.parent.containerType = LauncherLogProto.HOTSEAT;
+            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_PREDICTION)) {
+                event.srcTarget.parent.containerType = LauncherLogProto.PREDICTION;
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, String.format("parent bundle: %s %s %s %s",
+                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER),
+                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER_PAGE),
+                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER),
+                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE)));
+            }
+        }
+        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
+        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
+        processEvent(event);
+    }
+
+    /**
+     * Currently logs following containers: workspace, allapps, widget tray.
+     */
+    public final void resetElapsedContainerMillis() {
+        mElapsedContainerMillis = System.currentTimeMillis();
+        if(DEBUG) {
+            Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis);
+        }
+    }
+
+    public final void resetElapsedSessionMillis() {
+        mElapsedSessionMillis = System.currentTimeMillis();
+        mElapsedContainerMillis = System.currentTimeMillis();
+        if(DEBUG) {
+            Log.d(TAG, "resetElapsedSessionMillis " + mElapsedSessionMillis);
+        }
+    }
+
+    public final void resetActionDurationMillis() {
+        mActionDurationMillis = System.currentTimeMillis();
+        if(DEBUG) {
+            Log.d(TAG, "resetElapsedContainerMillis " + mElapsedContainerMillis);
+        }
+    }
+
+    public abstract void processEvent(LauncherLogProto.LauncherEvent ev);
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 19ec3ed..931466c 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -23,8 +23,8 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.backup.nano.BackupProtos;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.LongArrayMap;
 
 import java.util.ArrayList;
@@ -696,7 +696,8 @@
                         Favorites.SPANX,                // 4
                         Favorites.SPANY,                // 5
                         Favorites.INTENT,               // 6
-                        Favorites.APPWIDGET_PROVIDER},  // 7
+                        Favorites.APPWIDGET_PROVIDER,   // 7
+                        Favorites.APPWIDGET_ID},        // 8
                 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
                         + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
 
@@ -708,6 +709,7 @@
         final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
         final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
         final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
+        final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
 
         ArrayList<DbEntry> entries = new ArrayList<>();
         while (c.moveToNext()) {
@@ -737,9 +739,9 @@
                         entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
                                 * entry.spanX * entry.spanY);
 
-                        // Migration happens for current user only.
-                        LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo(
-                                mContext, cn, UserHandleCompat.myUserHandle());
+                        int widgetId = c.getInt(indexAppWidgetId);
+                        LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
+                                mContext).getLauncherAppWidgetInfo(widgetId);
                         Point spans = pInfo == null ?
                                 mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
                         if (spans != null) {
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 99a53ff..e043c94 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,9 +1,13 @@
 
 package com.android.launcher3.model;
 
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.os.DeadObjectException;
+import android.os.TransactionTooLargeException;
 import android.util.Log;
 
 import com.android.launcher3.AppFilter;
@@ -16,6 +20,7 @@
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -95,8 +100,41 @@
         return mRawList;
     }
 
-    public void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+    public boolean isEmpty() {
+        return mRawList.isEmpty();
+    }
+
+    public WidgetsModel updateAndClone(Context context) {
         Utilities.assertWorkerThread();
+
+        try {
+            final ArrayList<Object> widgetsAndShortcuts = new ArrayList<>();
+            // Widgets
+            for (AppWidgetProviderInfo widgetInfo :
+                    AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
+                widgetsAndShortcuts.add(LauncherAppWidgetProviderInfo
+                        .fromProviderInfo(context, widgetInfo));
+            }
+            // Shortcuts
+            widgetsAndShortcuts.addAll(context.getPackageManager().queryIntentActivities(
+                    new Intent(Intent.ACTION_CREATE_SHORTCUT), 0));
+            setWidgetsAndShortcuts(widgetsAndShortcuts);
+        } catch (Exception e) {
+            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
+                    (e.getCause() instanceof TransactionTooLargeException ||
+                            e.getCause() instanceof DeadObjectException)) {
+                // the returned value may be incomplete and will not be refreshed until the next
+                // time Launcher starts.
+                // TODO: after figuring out a repro step, introduce a dirty bit to check when
+                // onResume is called to refresh the widget provider list.
+            } else {
+                throw e;
+            }
+        }
+        return clone();
+    }
+
+    private void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
         mRawList = rawWidgetsShortcuts;
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 475762f..d5a340d 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -11,6 +11,7 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
+import com.android.launcher3.logging.UserEventLogger;
 import com.android.launcher3.allapps.AllAppsSearchBarController;
 import com.android.launcher3.util.ComponentKey;
 
@@ -225,6 +226,9 @@
         }
 
         @Override
+        public UserEventLogger getLogger() { return null; }
+
+        @Override
         public View getQsbBar() {
             return null;
         }
diff --git a/src/com/android/launcher3/userevent/Logger.java b/src/com/android/launcher3/userevent/Logger.java
deleted file mode 100644
index ae9041a..0000000
--- a/src/com/android/launcher3/userevent/Logger.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.android.launcher3.userevent;
-
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Stats;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.Locale;
-
-/**
- * Creates {@LauncherLogProto} nano protobuf object that can be used for user event
- * metrics analysis.
- */
-public class Logger {
-
-    private static final String TAG = "UserEventLogger";
-    private static final boolean DEBUG = false;
-
-    private long mActionDurationMillis;
-    private long mElapsedContainerMillis;
-    private long mElapsedSessionMillis;
-
-    private Context mContext;
-
-    public Logger(Context context) {
-        mContext = context;
-    }
-
-    public void logAppLaunch(String provider, ShortcutInfo shortcut, Bundle bundle) {
-        LauncherEvent event = new LauncherEvent();
-        event.action = new Action();
-        event.action.type = Action.TOUCH;
-        event.action.touch = Action.TAP;
-
-        event.srcTarget = new Target();
-        event.srcTarget.type = Target.ITEM;
-        event.srcTarget.itemType = LauncherLogProto.APP_ICON;
-        // TODO: package hash name should be different per device.
-        event.srcTarget.packageNameHash = provider.hashCode();
-
-        event.srcTarget.parent = new Target();
-        String subContainer = bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER);
-
-        if (shortcut != null) {
-            event.srcTarget.parent.containerType = getContainerType(shortcut);
-            event.srcTarget.pageIndex = (int) shortcut.screenId;
-            event.srcTarget.gridX = shortcut.cellX;
-            event.srcTarget.gridX = shortcut.cellY;
-        }
-        if (subContainer != null) {
-            event.srcTarget.parent.type = Target.CONTAINER;
-            if (subContainer.equals(Stats.SUB_CONTAINER_FOLDER)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.FOLDER;
-            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_A_Z)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.ALLAPPS;
-            } else if (subContainer.equals(Stats.CONTAINER_HOTSEAT)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.HOTSEAT;
-            } else if (subContainer.equals(Stats.SUB_CONTAINER_ALL_APPS_PREDICTION)) {
-                event.srcTarget.parent.containerType = LauncherLogProto.PREDICTION;
-            }
-
-            if (DEBUG) {
-                Log.d(TAG, String.format("parent bundle: %s %s %s %s",
-                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER),
-                        bundle.getString(Stats.SOURCE_EXTRA_CONTAINER_PAGE),
-                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER),
-                        bundle.getString(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE)));
-            }
-        }
-
-
-        // Assign timeToAction
-        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
-        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
-
-        // Debug
-        processLauncherEvent(event);
-    }
-
-    public void resetElapsedContainerMillis() {
-        mElapsedContainerMillis = System.currentTimeMillis();
-    }
-
-    public void resetElapsedSessionMillis() {
-        mElapsedSessionMillis = System.currentTimeMillis();
-    }
-
-    //
-    // Debugging helper methods.
-    // toString() cannot be overriden inside auto generated {@link LauncherLogProto}.
-    // Note: switch statement cannot be replaced with reflection as proguard strips the constants
-
-    private static void processLauncherEvent(LauncherEvent ev) {
-        if (DEBUG) {
-            if (ev.action.touch == Action.TAP && ev.srcTarget.itemType == LauncherLogProto.APP_ICON) {
-                Log.d(TAG, String.format(Locale.US, "action:%s target:%s\n\telapsed container %d ms session %d ms",
-                        getActionStr(ev.action),
-                        getTargetStr(ev.srcTarget),
-                        ev.elapsedContainerMillis,
-                        ev.elapsedSessionMillis));
-            }
-        }
-    }
-
-    private static int getContainerType(ShortcutInfo shortcut) {
-        switch ((int) shortcut.container) {
-            case LauncherSettings.Favorites.CONTAINER_DESKTOP: return LauncherLogProto.WORKSPACE;
-            case LauncherSettings.Favorites.CONTAINER_HOTSEAT: return LauncherLogProto.HOTSEAT;
-            default:
-                return (int) shortcut.container;
-        }
-    }
-
-    private static String getActionStr(Action action) {
-        switch(action.touch) {
-            case Action.TAP: return "TAP";
-            case Action.LONGPRESS: return "LONGPRESS";
-            case Action.DRAGDROP: return "DRAGDROP";
-            case Action.PINCH: return "PINCH";
-            default: return "UNKNOWN";
-        }
-    }
-
-    private static String getTargetStr(Target t) {
-        String typeStr;
-        switch (t.type) {
-            case LauncherLogProto.Target.ITEM:
-                return getItemStr(t);
-            case LauncherLogProto.Target.CONTROL:
-                return getControlStr(t);
-            case LauncherLogProto.Target.CONTAINER:
-                return getContainerStr(t);
-            default:
-                return "UNKNOWN TARGET TYPE";
-        }
-    }
-
-    private static String getItemStr(Target t) {
-        String typeStr = "";
-        switch(t.itemType){
-            case LauncherLogProto.APP_ICON: typeStr = "ICON"; break;
-            case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
-            case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break;
-            default: typeStr = "UNKNOWN";
-        }
-
-        return typeStr + " " + t.packageNameHash + " grid=(" + t.gridX + "," + t.gridY + ") "
-                + getContainerStr(t.parent);
-    }
-
-    private static String getControlStr(Target t) {
-        switch(t.controlType) {
-            case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON";
-            case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON";
-            case LauncherLogProto.WALLPAPER_BUTTON: return "WALLPAPER_BUTTON";
-            case LauncherLogProto.SETTINGS_BUTTON: return "SETTINGS_BUTTON";
-            case LauncherLogProto.REMOVE_TARGET: return "REMOVE_TARGET";
-            case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET";
-            case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET";
-            case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE";
-            case LauncherLogProto.FAST_SCROLL_HANDLE: return "FAST_SCROLL_HANDLE";
-            default: return "UNKNOWN";
-        }
-    }
-
-    private static String getContainerStr(Target t) {
-        String str;
-        Log.d(TAG, "t.containerType" + t.containerType);
-        switch (t.containerType) {
-            case LauncherLogProto.WORKSPACE:
-                str = "WORKSPACE";
-                break;
-            case LauncherLogProto.HOTSEAT:
-                str = "HOTSEAT";
-                break;
-            case LauncherLogProto.FOLDER:
-                str = "FOLDER";
-                break;
-            case LauncherLogProto.ALLAPPS:
-                str = "ALLAPPS";
-                break;
-            case LauncherLogProto.WIDGETS:
-                str = "WIDGETS";
-                break;
-            case LauncherLogProto.OVERVIEW:
-                str = "OVERVIEW";
-                break;
-            case LauncherLogProto.PREDICTION:
-                str = "PREDICTION";
-                break;
-            case LauncherLogProto.SEARCHRESULT:
-                str = "SEARCHRESULT";
-                break;
-            default:
-                str = "UNKNOWN";
-        }
-        return str + " id=" + t.pageIndex;
-    }
-}
-
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index fc2e0be..5d6f475 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -335,6 +335,10 @@
         mAdapter.notifyDataSetChanged();
     }
 
+    public boolean isEmpty() {
+        return mAdapter.getItemCount() == 0;
+    }
+
     private WidgetPreviewLoader getWidgetPreviewLoader() {
         if (mWidgetPreviewLoader == null) {
             mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();