From 2e1efb480a9b77a97cb623d4f5faf6802a417422 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 3 Mar 2016 16:58:55 -0800 Subject: [PATCH] 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 --- src/com/android/launcher3/Launcher.java | 89 ++++--- .../launcher3/LauncherAppWidgetHost.java | 4 + .../launcher3/LauncherBackupHelper.java | 25 +- src/com/android/launcher3/LauncherModel.java | 221 ++++-------------- .../LauncherStateTransitionAnimation.java | 2 +- src/com/android/launcher3/Stats.java | 4 +- src/com/android/launcher3/Workspace.java | 49 ++-- .../compat/AppWidgetManagerCompat.java | 12 + .../compat/AppWidgetManagerCompatV16.java | 24 ++ .../compat/AppWidgetManagerCompatVL.java | 27 +++ .../model/GridSizeMigrationTask.java | 12 +- .../android/launcher3/model/WidgetsModel.java | 40 +++- .../widget/WidgetsContainerView.java | 4 + 13 files changed, 265 insertions(+), 248 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index fc828da7b2..2b03dc5883 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 b07ccc362e..8c23ff30d7 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 05d729e78a..cad0f2c25a 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 f707ec5018..3877b9496a 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 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 sBgWorkspaceScreens = new ArrayList(); - // sBgWidgetProviders is the set of widget providers including custom internal widgets - public static HashMap sBgWidgetProviders; - - // sBgShortcutProviders is the set of custom shortcut providers - public static List sBgShortcutProviders; - // sPendingPackages is a set of packages which could be on sdcard and are not available yet static final HashMap> sPendingPackages = new HashMap>(); @@ -209,7 +197,8 @@ public class LauncherModel extends BroadcastReceiver public void bindRestoreItemsChange(HashSet updates); public void bindComponentsRemoved(ArrayList packageNames, ArrayList 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 itemsToRemove = new ArrayList(); - final ArrayList restoredRows = new ArrayList(); + final ArrayList itemsToRemove = new ArrayList<>(); + final ArrayList 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 occupied = new LongArrayMap<>(); + HashMap 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,167 +3217,49 @@ 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 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 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 getWidgetProviders(Context context, - boolean refresh) { - ArrayList results = - new ArrayList(); - try { - synchronized (sBgLock) { - if (sBgWidgetProviders == null || refresh) { - HashMap tmpWidgetProviders - = new HashMap<>(); - AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context); - LauncherAppWidgetProviderInfo info; - - List 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 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 widgetsAndShortcuts = new ArrayList(); - widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh)); - - // Update shortcut providers - synchronized (sBgLock) { - try { - Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); - List 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 54945be3d4..1df29561ed 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 2873828699..a5e1bf8da6 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 a74fa0dbc5..0b72ef7937 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 changedInfo) { + public void widgetsRestored(ArrayList 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 f0221bc240..811cacfc09 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 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 getAllProvidersMap(); } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java index e9d2510e8e..de9414ed27 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 getAllProvidersMap() { + HashMap 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 3bc3d0d80c..a1570e66a1 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 getAllProvidersMap() { + HashMap 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 19ec3ed648..931466c3b1 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 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 99a53ff369..e043c948f3 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 rawWidgetsShortcuts) { + public boolean isEmpty() { + return mRawList.isEmpty(); + } + + public WidgetsModel updateAndClone(Context context) { Utilities.assertWorkerThread(); + + try { + final ArrayList 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 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 d7ed9ed6b9..2a08d2ddd8 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();