summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2016-03-03 16:58:55 -0800
committerSunny Goyal <sunnygoyal@google.com>2016-03-09 20:21:22 -0800
commit2e1efb480a9b77a97cb623d4f5faf6802a417422 (patch)
tree8c6c0f0f7d91c7fc7d4d8ca23092f5fa4be720c9 /src/com
parent726eb822d38ad422f37b74c4b0fc504a423ab926 (diff)
downloadandroid_packages_apps_Trebuchet-2e1efb480a9b77a97cb623d4f5faf6802a417422.tar.gz
android_packages_apps_Trebuchet-2e1efb480a9b77a97cb623d4f5faf6802a417422.tar.bz2
android_packages_apps_Trebuchet-2e1efb480a9b77a97cb623d4f5faf6802a417422.zip
Changing the widget loading strategy
Widget is loaded only when the user enters the overview mode and we keep the list updated as long as the user is in the overview mode. Once the user leaves the overview mode, we stop responding to widget updates Bug: 26077457 Change-Id: I9e4904b8f1300bfe0d77e2bc5f59aa6963fad8d1
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/launcher3/Launcher.java89
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java4
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java25
-rw-r--r--src/com/android/launcher3/LauncherModel.java213
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java2
-rw-r--r--src/com/android/launcher3/Stats.java4
-rw-r--r--src/com/android/launcher3/Workspace.java49
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompat.java12
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java24
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java27
-rw-r--r--src/com/android/launcher3/model/GridSizeMigrationTask.java12
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java40
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java4
13 files changed, 261 insertions, 244 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fc828da7b..2b03dc588 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,7 +82,6 @@ import android.view.View;
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;
@@ -1530,8 +1529,7 @@ public class Launcher extends Activity
ItemInfo info = mPendingAddInfo;
if (appWidgetInfo == null) {
- appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
- mAppWidgetManager.getAppWidgetInfo(appWidgetId));
+ appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
if (appWidgetInfo.isCustomWidget) {
@@ -2528,10 +2526,10 @@ public class Launcher extends Activity
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;
@@ -3509,10 +3507,6 @@ public class Launcher extends Activity
// TODO
}
- protected void disableVoiceButtonProxy(boolean disable) {
- // NO-OP
- }
-
public boolean launcherCallbacksProvidesSearch() {
return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
}
@@ -3926,6 +3920,16 @@ public class Launcher extends Activity
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 +3945,31 @@ public class Launcher extends Activity
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 +3981,7 @@ public class Launcher extends Activity
}
// 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 +4017,42 @@ public class Launcher extends Activity
: 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 +4390,15 @@ public class Launcher extends Activity
}
}
- 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 +4409,13 @@ public class Launcher extends Activity
}
}
+ @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 b07ccc362..8c23ff30d 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -102,6 +102,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
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 05d729e78..cad0f2c25 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.Key;
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 @@ public class LauncherBackupHelper implements BackupHelper {
+ 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 @@ public class LauncherBackupHelper implements BackupHelper {
} 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 @@ public class LauncherBackupHelper implements BackupHelper {
}
/** 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 @@ public class LauncherBackupHelper implements BackupHelper {
Point spans = info.getMinSpans(mIdp, mContext);
widget.minSpanX = spans.x;
widget.minSpanY = spans.y;
-
return widget;
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f707ec501..3877b9496 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.content.pm.ResolveInfo;
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.Looper;
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.net.URISyntaxException;
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 @@ public class LauncherModel extends BroadcastReceiver
@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 @@ public class LauncherModel extends BroadcastReceiver
// 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 class LauncherModel extends BroadcastReceiver
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 @@ public class LauncherModel extends BroadcastReceiver
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 @@ public class LauncherModel extends BroadcastReceiver
.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 @@ public class LauncherModel extends BroadcastReceiver
// 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 @@ public class LauncherModel extends BroadcastReceiver
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 @@ public class LauncherModel extends BroadcastReceiver
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 @@ public class LauncherModel extends BroadcastReceiver
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 @@ public class LauncherModel extends BroadcastReceiver
});
// 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 @@ public class LauncherModel extends BroadcastReceiver
}
});
}
-
- // 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,169 +3217,51 @@ public class LauncherModel extends BroadcastReceiver
});
}
- // 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);
+ // 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() {
+ public void run() {
+ Callbacks cb = getCallback();
+ if (callbacks == cb && cb != null) {
+ callbacks.notifyWidgetProvidersChanged();
}
}
- }
-
- 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;
+ 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);
}
- } 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) {
-
+ public void refreshAndBindWidgetsAndShortcuts(
+ final Callbacks callbacks, final boolean bindFirst) {
runOnWorkerThread(new Runnable() {
@Override
public void run() {
- updateWidgetsModel(refresh);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAllPackages(mFgWidgetsModel);
- }
- }
- });
+ 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(
- mFgWidgetsModel.getRawList());
+ model.getRawList());
}
});
}
- /**
- * 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;
- }
- }
- if (sBgShortcutProviders != null) {
- widgetsAndShortcuts.addAll(sBgShortcutProviders);
- }
- }
- mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
- mFgWidgetsModel = mBgWidgetsModel.clone();
- }
-
@Thunk static boolean isPackageDisabled(Context context, String packageName,
UserHandleCompat user) {
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 54945be3d..1df29561e 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -397,7 +397,7 @@ public class LauncherStateTransitionAnimation {
// Animate the search bar
final SearchDropTargetBar.State toSearchBarState =
- toWorkspaceState.getSearchDropTargetBarState();
+ toWorkspaceState.searchDropTargetBarState;
mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
animated ? revealDuration : 0, animation);
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index 287382869..a5e1bf8da 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -146,6 +146,8 @@ public class Stats {
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 a74fa0dbc..0b72ef793 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,11 +52,11 @@ import android.view.View;
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 @@ public class Workspace extends PagedView
// 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 @@ public class Workspace extends PagedView
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 @@ public class Workspace extends PagedView
invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
}
+ if (shouldNotifyWidgetChange) {
+ mLauncher.notifyWidgetProvidersChanged();
+ }
+
return workspaceAnim;
}
@@ -4148,13 +4152,22 @@ public class Workspace extends PagedView
});
}
- 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/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index f0221bc24..811cacfc0 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -20,6 +20,7 @@ import android.app.Activity;
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 android.os.Bundle;
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 @@ public abstract class AppWidgetManagerCompat {
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 class AppWidgetManagerCompat {
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 e9d2510e8..de9414ed2 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -21,6 +21,7 @@ import android.app.Activity;
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 android.os.Bundle;
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 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
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 3bc3d0d80..a1570e66a 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -21,6 +21,7 @@ import android.app.Activity;
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 android.widget.Toast;
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 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
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/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 19ec3ed64..931466c3b 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;
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 @@ public class GridSizeMigrationTask {
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 @@ public class GridSizeMigrationTask {
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 @@ public class GridSizeMigrationTask {
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 99a53ff36..e043c948f 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.Utilities;
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 @@ public class WidgetsModel {
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/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index d7ed9ed6b..2a08d2ddd 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -327,6 +327,10 @@ public class WidgetsContainerView extends BaseContainerView
mAdapter.notifyDataSetChanged();
}
+ public boolean isEmpty() {
+ return mAdapter.getItemCount() == 0;
+ }
+
private WidgetPreviewLoader getWidgetPreviewLoader() {
if (mWidgetPreviewLoader == null) {
mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();