Loading core/java/android/appwidget/AppWidgetManager.java +55 −34 Original line number Original line Diff line number Diff line Loading @@ -562,13 +562,6 @@ public class AppWidgetManager { }); }); } } private boolean isPostingTaskToBackground(@Nullable RemoteViews views) { return Looper.myLooper() == Looper.getMainLooper() && RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); } /** /** * Set the RemoteViews to use for the specified appWidgetIds. * Set the RemoteViews to use for the specified appWidgetIds. * <p> * <p> Loading @@ -593,10 +586,18 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error updating app widget views in background", e); Log.e(TAG, "Error updating app widget views in background", e); } } Loading @@ -604,6 +605,7 @@ public class AppWidgetManager { return; return; } } } try { try { mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); Loading Loading @@ -714,10 +716,18 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error partially updating app widget views in background", e); Log.e(TAG, "Error partially updating app widget views in background", e); } } Loading @@ -725,6 +735,7 @@ public class AppWidgetManager { return; return; } } } try { try { mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); Loading Loading @@ -782,17 +793,27 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.updateAppWidgetProvider(provider, views); mService.updateAppWidgetProvider(provider, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error updating app widget view using provider in background", e); Log.e(TAG, "Error updating app widget view using provider in background", e); } } }); }); return; return; } } } try { try { mService.updateAppWidgetProvider(provider, views); mService.updateAppWidgetProvider(provider, views); Loading core/java/android/widget/RemoteViews.java +195 −25 Original line number Original line Diff line number Diff line Loading @@ -85,6 +85,7 @@ import android.util.Log; import android.util.LongArray; import android.util.LongArray; import android.util.Pair; import android.util.Pair; import android.util.SizeF; import android.util.SizeF; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.util.TypedValue.ComplexDimensionUnit; Loading Loading @@ -367,6 +368,11 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage @UnsupportedAppUsage private BitmapCache mBitmapCache = new BitmapCache(); private BitmapCache mBitmapCache = new BitmapCache(); /** * Maps Intent ID to RemoteCollectionItems to avoid duplicate items */ private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache(); /** Cache of ApplicationInfos used by collection items. */ /** Cache of ApplicationInfos used by collection items. */ private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache(); private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache(); Loading Loading @@ -784,9 +790,12 @@ public class RemoteViews implements Parcelable, Filter { if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction && itemsAction.mViewId == viewId && itemsAction.mViewId == viewId && itemsAction.mServiceIntent != null) { && itemsAction.mServiceIntent != null) { mActions.set(i, SetRemoteCollectionItemListAdapterAction newCollectionAction = new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId, new SetRemoteCollectionItemListAdapterAction( itemsAction.mServiceIntent)); itemsAction.mViewId, itemsAction.mServiceIntent); newCollectionAction.mIntentId = itemsAction.mIntentId; newCollectionAction.mIsReplacedIntoAction = true; mActions.set(i, newCollectionAction); isActionReplaced = true; isActionReplaced = true; } else if (action instanceof SetRemoteViewsAdapterIntent intentAction } else if (action instanceof SetRemoteViewsAdapterIntent intentAction && intentAction.mViewId == viewId) { && intentAction.mViewId == viewId) { Loading Loading @@ -1048,6 +1057,8 @@ public class RemoteViews implements Parcelable, Filter { @NonNull @NonNull private CompletableFuture<RemoteCollectionItems> mItemsFuture; private CompletableFuture<RemoteCollectionItems> mItemsFuture; final Intent mServiceIntent; final Intent mServiceIntent; int mIntentId = -1; boolean mIsReplacedIntoAction = false; SetRemoteCollectionItemListAdapterAction(@IdRes int id, SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { @NonNull RemoteCollectionItems items) { Loading Loading @@ -1108,38 +1119,36 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); mViewId = parcel.readInt(); mItemsFuture = CompletableFuture.completedFuture( mIntentId = parcel.readInt(); new RemoteCollectionItems(parcel, getHierarchyRootData())); mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1 ? null : new RemoteCollectionItems(parcel, getHierarchyRootData())); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); } } @Override @Override public void setHierarchyRootData(HierarchyRootData rootData) { public void setHierarchyRootData(HierarchyRootData rootData) { if (mIntentId == -1) { mItemsFuture = mItemsFuture mItemsFuture = mItemsFuture .thenApply(rc -> { .thenApply(rc -> { rc.setHierarchyRootData(rootData); rc.setHierarchyRootData(rootData); return rc; return rc; }); }); return; } } private static RemoteCollectionItems getCollectionItemsFromFuture( // Set the root data for items in the cache instead CompletableFuture<RemoteCollectionItems> itemsFuture) { mCollectionCache.setHierarchyDataForId(mIntentId, rootData); RemoteCollectionItems items; try { items = itemsFuture.get(); } catch (Exception e) { Log.e(LOG_TAG, "Error getting collection items from future", e); items = new RemoteCollectionItems.Builder().build(); } return items; } } @Override @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); dest.writeInt(mViewId); dest.writeInt(mIntentId); if (mIntentId == -1) { RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); items.writeToParcel(dest, flags, /* attached= */ true); items.writeToParcel(dest, flags, /* attached= */ true); } dest.writeTypedObject(mServiceIntent, flags); dest.writeTypedObject(mServiceIntent, flags); } } Loading @@ -1149,7 +1158,9 @@ public class RemoteViews implements Parcelable, Filter { View target = root.findViewById(mViewId); View target = root.findViewById(mViewId); if (target == null) return; if (target == null) return; RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); RemoteCollectionItems items = mIntentId == -1 ? getCollectionItemsFromFuture(mItemsFuture) : mCollectionCache.getItemsForId(mIntentId); // Ensure that we are applying to an AppWidget root // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { if (!(rootParent instanceof AppWidgetHostView)) { Loading Loading @@ -1210,6 +1221,153 @@ public class RemoteViews implements Parcelable, Filter { } } } } private static RemoteCollectionItems getCollectionItemsFromFuture( CompletableFuture<RemoteCollectionItems> itemsFuture) { RemoteCollectionItems items; try { items = itemsFuture.get(); } catch (Exception e) { Log.e(LOG_TAG, "Error getting collection items from future", e); items = new RemoteCollectionItems.Builder().build(); } return items; } /** * @hide */ public void collectAllIntents() { mCollectionCache.collectAllIntentsNoComplete(this); } private class RemoteCollectionCache { private SparseArray<String> mIdToUriMapping = new SparseArray<>(); private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>(); // We don't put this into the parcel private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping = new HashMap<>(); RemoteCollectionCache() { } RemoteCollectionCache(RemoteCollectionCache src) { boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0; for (int i = 0; i < src.mIdToUriMapping.size(); i++) { String uri = src.mIdToUriMapping.valueAt(i); mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri); if (isWaitingCache) { mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri)); } else { mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri)); } } } RemoteCollectionCache(Parcel in) { int cacheSize = in.readInt(); HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache, this, mApplicationInfoCache, mClassCookies); for (int i = 0; i < cacheSize; i++) { int intentId = in.readInt(); String intentUri = in.readString8(); RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData); mIdToUriMapping.put(intentId, intentUri); mUriToCollectionMapping.put(intentUri, items); } } void setHierarchyDataForId(int intentId, HierarchyRootData data) { String uri = mIdToUriMapping.get(intentId); if (mTempUriToFutureMapping.get(uri) != null) { CompletableFuture<RemoteCollectionItems> itemsFuture = mTempUriToFutureMapping.get(uri); mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> { rc.setHierarchyRootData(data); return rc; })); return; } RemoteCollectionItems items = mUriToCollectionMapping.get(uri); items.setHierarchyRootData(data); } RemoteCollectionItems getItemsForId(int intentId) { String uri = mIdToUriMapping.get(intentId); return mUriToCollectionMapping.get(uri); } void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { if (inViews.hasSizedRemoteViews()) { for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { remoteViews.collectAllIntents(); } } else if (inViews.hasLandscapeAndPortraitLayouts()) { inViews.mLandscape.collectAllIntents(); inViews.mPortrait.collectAllIntents(); } else if (inViews.mActions != null) { for (Action action : inViews.mActions) { if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { // Deal with the case where the intent is replaced into the action list if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) { continue; } if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { String uri = mIdToUriMapping.get(rca.mIntentId); mTempUriToFutureMapping.put(uri, rca.mItemsFuture); rca.mItemsFuture = CompletableFuture.completedFuture(null); continue; } // Differentiate between the normal collection actions and the ones with // intents. if (rca.mServiceIntent != null) { String uri = rca.mServiceIntent.toUri(0); int index = mIdToUriMapping.indexOfValue(uri); if (index == -1) { int newIntentId = mIdToUriMapping.size(); rca.mIntentId = newIntentId; mIdToUriMapping.put(newIntentId, uri); // mUriToIntentMapping.put(uri, mServiceIntent); mTempUriToFutureMapping.put(uri, rca.mItemsFuture); } else { rca.mIntentId = mIdToUriMapping.keyAt(index); } rca.mItemsFuture = CompletableFuture.completedFuture(null); } else { RemoteCollectionItems items = getCollectionItemsFromFuture( rca.mItemsFuture); for (RemoteViews views : items.mViews) { views.collectAllIntents(); } } } else if (action instanceof ViewGroupActionAdd vgaa && vgaa.mNestedViews != null) { vgaa.mNestedViews.collectAllIntents(); } } } } public void writeToParcel(Parcel out, int flags) { out.writeInt(mIdToUriMapping.size()); for (int i = 0; i < mIdToUriMapping.size(); i++) { out.writeInt(mIdToUriMapping.keyAt(i)); String intentUri = mIdToUriMapping.valueAt(i); out.writeString8(intentUri); RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri)) : mUriToCollectionMapping.get(intentUri); items.writeToParcel(out, flags, true); } } } private class SetRemoteViewsAdapterIntent extends Action { private class SetRemoteViewsAdapterIntent extends Action { Intent mIntent; Intent mIntent; boolean mIsAsync = false; boolean mIsAsync = false; Loading Loading @@ -3850,9 +4008,12 @@ public class RemoteViews implements Parcelable, Filter { private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { if (hierarchyRoot == null) { if (hierarchyRoot == null) { mBitmapCache = src.mBitmapCache; mBitmapCache = src.mBitmapCache; // We need to create a new instance because we don't reconstruct collection cache mCollectionCache = new RemoteCollectionCache(src.mCollectionCache); mApplicationInfoCache = src.mApplicationInfoCache; mApplicationInfoCache = src.mApplicationInfoCache; } else { } else { mBitmapCache = hierarchyRoot.mBitmapCache; mBitmapCache = hierarchyRoot.mBitmapCache; mCollectionCache = hierarchyRoot.mCollectionCache; mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; } } if (hierarchyRoot == null || src.mIsRoot) { if (hierarchyRoot == null || src.mIsRoot) { Loading Loading @@ -3926,6 +4087,7 @@ public class RemoteViews implements Parcelable, Filter { mBitmapCache = new BitmapCache(parcel); mBitmapCache = new BitmapCache(parcel); // Store the class cookies such that they are available when we clone this RemoteView. // Store the class cookies such that they are available when we clone this RemoteView. mClassCookies = parcel.copyClassCookies(); mClassCookies = parcel.copyClassCookies(); mCollectionCache = new RemoteCollectionCache(parcel); } else { } else { configureAsChild(rootData); configureAsChild(rootData); } } Loading Loading @@ -4087,6 +4249,7 @@ public class RemoteViews implements Parcelable, Filter { private void configureAsChild(@NonNull HierarchyRootData rootData) { private void configureAsChild(@NonNull HierarchyRootData rootData) { mIsRoot = false; mIsRoot = false; mBitmapCache = rootData.mBitmapCache; mBitmapCache = rootData.mBitmapCache; mCollectionCache = rootData.mRemoteCollectionCache; mApplicationInfoCache = rootData.mApplicationInfoCache; mApplicationInfoCache = rootData.mApplicationInfoCache; mClassCookies = rootData.mClassCookies; mClassCookies = rootData.mClassCookies; configureDescendantsAsChildren(); configureDescendantsAsChildren(); Loading Loading @@ -6357,6 +6520,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. // is shared by all children. if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } mApplication.writeToParcel(dest, flags); mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { if (mIsRoot || mIdealSize == null) { Loading @@ -6373,6 +6537,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } dest.writeInt(mSizedRemoteViews.size()); dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { for (RemoteViews view : mSizedRemoteViews) { Loading @@ -6384,6 +6549,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. // is shared by all children. if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } mLandscape.writeToParcel(dest, flags); mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user // Both RemoteViews already share the same package and user Loading Loading @@ -7262,19 +7428,23 @@ public class RemoteViews implements Parcelable, Filter { } } private HierarchyRootData getHierarchyRootData() { private HierarchyRootData getHierarchyRootData() { return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies); return new HierarchyRootData(mBitmapCache, mCollectionCache, mApplicationInfoCache, mClassCookies); } } private static final class HierarchyRootData { private static final class HierarchyRootData { final BitmapCache mBitmapCache; final BitmapCache mBitmapCache; final RemoteCollectionCache mRemoteCollectionCache; final ApplicationInfoCache mApplicationInfoCache; final ApplicationInfoCache mApplicationInfoCache; final Map<Class, Object> mClassCookies; final Map<Class, Object> mClassCookies; HierarchyRootData( HierarchyRootData( BitmapCache bitmapCache, BitmapCache bitmapCache, RemoteCollectionCache remoteCollectionCache, ApplicationInfoCache applicationInfoCache, ApplicationInfoCache applicationInfoCache, Map<Class, Object> classCookies) { Map<Class, Object> classCookies) { mBitmapCache = bitmapCache; mBitmapCache = bitmapCache; mRemoteCollectionCache = remoteCollectionCache; mApplicationInfoCache = applicationInfoCache; mApplicationInfoCache = applicationInfoCache; mClassCookies = classCookies; mClassCookies = classCookies; } } Loading Loading
core/java/android/appwidget/AppWidgetManager.java +55 −34 Original line number Original line Diff line number Diff line Loading @@ -562,13 +562,6 @@ public class AppWidgetManager { }); }); } } private boolean isPostingTaskToBackground(@Nullable RemoteViews views) { return Looper.myLooper() == Looper.getMainLooper() && RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); } /** /** * Set the RemoteViews to use for the specified appWidgetIds. * Set the RemoteViews to use for the specified appWidgetIds. * <p> * <p> Loading @@ -593,10 +586,18 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error updating app widget views in background", e); Log.e(TAG, "Error updating app widget views in background", e); } } Loading @@ -604,6 +605,7 @@ public class AppWidgetManager { return; return; } } } try { try { mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); Loading Loading @@ -714,10 +716,18 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error partially updating app widget views in background", e); Log.e(TAG, "Error partially updating app widget views in background", e); } } Loading @@ -725,6 +735,7 @@ public class AppWidgetManager { return; return; } } } try { try { mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); Loading Loading @@ -782,17 +793,27 @@ public class AppWidgetManager { return; return; } } if (isPostingTaskToBackground(views)) { final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() && (mHasPostedLegacyLists = mHasPostedLegacyLists || (views != null && views.hasLegacyLists())); if (isConvertingAdapter) { views.collectAllIntents(); if (Looper.getMainLooper() == Looper.myLooper()) { RemoteViews viewsCopy = new RemoteViews(views); createUpdateExecutorIfNull().execute(() -> { createUpdateExecutorIfNull().execute(() -> { try { try { mService.updateAppWidgetProvider(provider, views); mService.updateAppWidgetProvider(provider, viewsCopy); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "Error updating app widget view using provider in background", e); Log.e(TAG, "Error updating app widget view using provider in background", e); } } }); }); return; return; } } } try { try { mService.updateAppWidgetProvider(provider, views); mService.updateAppWidgetProvider(provider, views); Loading
core/java/android/widget/RemoteViews.java +195 −25 Original line number Original line Diff line number Diff line Loading @@ -85,6 +85,7 @@ import android.util.Log; import android.util.LongArray; import android.util.LongArray; import android.util.Pair; import android.util.Pair; import android.util.SizeF; import android.util.SizeF; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.util.TypedValue.ComplexDimensionUnit; Loading Loading @@ -367,6 +368,11 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage @UnsupportedAppUsage private BitmapCache mBitmapCache = new BitmapCache(); private BitmapCache mBitmapCache = new BitmapCache(); /** * Maps Intent ID to RemoteCollectionItems to avoid duplicate items */ private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache(); /** Cache of ApplicationInfos used by collection items. */ /** Cache of ApplicationInfos used by collection items. */ private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache(); private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache(); Loading Loading @@ -784,9 +790,12 @@ public class RemoteViews implements Parcelable, Filter { if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction && itemsAction.mViewId == viewId && itemsAction.mViewId == viewId && itemsAction.mServiceIntent != null) { && itemsAction.mServiceIntent != null) { mActions.set(i, SetRemoteCollectionItemListAdapterAction newCollectionAction = new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId, new SetRemoteCollectionItemListAdapterAction( itemsAction.mServiceIntent)); itemsAction.mViewId, itemsAction.mServiceIntent); newCollectionAction.mIntentId = itemsAction.mIntentId; newCollectionAction.mIsReplacedIntoAction = true; mActions.set(i, newCollectionAction); isActionReplaced = true; isActionReplaced = true; } else if (action instanceof SetRemoteViewsAdapterIntent intentAction } else if (action instanceof SetRemoteViewsAdapterIntent intentAction && intentAction.mViewId == viewId) { && intentAction.mViewId == viewId) { Loading Loading @@ -1048,6 +1057,8 @@ public class RemoteViews implements Parcelable, Filter { @NonNull @NonNull private CompletableFuture<RemoteCollectionItems> mItemsFuture; private CompletableFuture<RemoteCollectionItems> mItemsFuture; final Intent mServiceIntent; final Intent mServiceIntent; int mIntentId = -1; boolean mIsReplacedIntoAction = false; SetRemoteCollectionItemListAdapterAction(@IdRes int id, SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { @NonNull RemoteCollectionItems items) { Loading Loading @@ -1108,38 +1119,36 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); mViewId = parcel.readInt(); mItemsFuture = CompletableFuture.completedFuture( mIntentId = parcel.readInt(); new RemoteCollectionItems(parcel, getHierarchyRootData())); mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1 ? null : new RemoteCollectionItems(parcel, getHierarchyRootData())); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); } } @Override @Override public void setHierarchyRootData(HierarchyRootData rootData) { public void setHierarchyRootData(HierarchyRootData rootData) { if (mIntentId == -1) { mItemsFuture = mItemsFuture mItemsFuture = mItemsFuture .thenApply(rc -> { .thenApply(rc -> { rc.setHierarchyRootData(rootData); rc.setHierarchyRootData(rootData); return rc; return rc; }); }); return; } } private static RemoteCollectionItems getCollectionItemsFromFuture( // Set the root data for items in the cache instead CompletableFuture<RemoteCollectionItems> itemsFuture) { mCollectionCache.setHierarchyDataForId(mIntentId, rootData); RemoteCollectionItems items; try { items = itemsFuture.get(); } catch (Exception e) { Log.e(LOG_TAG, "Error getting collection items from future", e); items = new RemoteCollectionItems.Builder().build(); } return items; } } @Override @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); dest.writeInt(mViewId); dest.writeInt(mIntentId); if (mIntentId == -1) { RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); items.writeToParcel(dest, flags, /* attached= */ true); items.writeToParcel(dest, flags, /* attached= */ true); } dest.writeTypedObject(mServiceIntent, flags); dest.writeTypedObject(mServiceIntent, flags); } } Loading @@ -1149,7 +1158,9 @@ public class RemoteViews implements Parcelable, Filter { View target = root.findViewById(mViewId); View target = root.findViewById(mViewId); if (target == null) return; if (target == null) return; RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); RemoteCollectionItems items = mIntentId == -1 ? getCollectionItemsFromFuture(mItemsFuture) : mCollectionCache.getItemsForId(mIntentId); // Ensure that we are applying to an AppWidget root // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { if (!(rootParent instanceof AppWidgetHostView)) { Loading Loading @@ -1210,6 +1221,153 @@ public class RemoteViews implements Parcelable, Filter { } } } } private static RemoteCollectionItems getCollectionItemsFromFuture( CompletableFuture<RemoteCollectionItems> itemsFuture) { RemoteCollectionItems items; try { items = itemsFuture.get(); } catch (Exception e) { Log.e(LOG_TAG, "Error getting collection items from future", e); items = new RemoteCollectionItems.Builder().build(); } return items; } /** * @hide */ public void collectAllIntents() { mCollectionCache.collectAllIntentsNoComplete(this); } private class RemoteCollectionCache { private SparseArray<String> mIdToUriMapping = new SparseArray<>(); private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>(); // We don't put this into the parcel private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping = new HashMap<>(); RemoteCollectionCache() { } RemoteCollectionCache(RemoteCollectionCache src) { boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0; for (int i = 0; i < src.mIdToUriMapping.size(); i++) { String uri = src.mIdToUriMapping.valueAt(i); mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri); if (isWaitingCache) { mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri)); } else { mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri)); } } } RemoteCollectionCache(Parcel in) { int cacheSize = in.readInt(); HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache, this, mApplicationInfoCache, mClassCookies); for (int i = 0; i < cacheSize; i++) { int intentId = in.readInt(); String intentUri = in.readString8(); RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData); mIdToUriMapping.put(intentId, intentUri); mUriToCollectionMapping.put(intentUri, items); } } void setHierarchyDataForId(int intentId, HierarchyRootData data) { String uri = mIdToUriMapping.get(intentId); if (mTempUriToFutureMapping.get(uri) != null) { CompletableFuture<RemoteCollectionItems> itemsFuture = mTempUriToFutureMapping.get(uri); mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> { rc.setHierarchyRootData(data); return rc; })); return; } RemoteCollectionItems items = mUriToCollectionMapping.get(uri); items.setHierarchyRootData(data); } RemoteCollectionItems getItemsForId(int intentId) { String uri = mIdToUriMapping.get(intentId); return mUriToCollectionMapping.get(uri); } void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { if (inViews.hasSizedRemoteViews()) { for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { remoteViews.collectAllIntents(); } } else if (inViews.hasLandscapeAndPortraitLayouts()) { inViews.mLandscape.collectAllIntents(); inViews.mPortrait.collectAllIntents(); } else if (inViews.mActions != null) { for (Action action : inViews.mActions) { if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { // Deal with the case where the intent is replaced into the action list if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) { continue; } if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { String uri = mIdToUriMapping.get(rca.mIntentId); mTempUriToFutureMapping.put(uri, rca.mItemsFuture); rca.mItemsFuture = CompletableFuture.completedFuture(null); continue; } // Differentiate between the normal collection actions and the ones with // intents. if (rca.mServiceIntent != null) { String uri = rca.mServiceIntent.toUri(0); int index = mIdToUriMapping.indexOfValue(uri); if (index == -1) { int newIntentId = mIdToUriMapping.size(); rca.mIntentId = newIntentId; mIdToUriMapping.put(newIntentId, uri); // mUriToIntentMapping.put(uri, mServiceIntent); mTempUriToFutureMapping.put(uri, rca.mItemsFuture); } else { rca.mIntentId = mIdToUriMapping.keyAt(index); } rca.mItemsFuture = CompletableFuture.completedFuture(null); } else { RemoteCollectionItems items = getCollectionItemsFromFuture( rca.mItemsFuture); for (RemoteViews views : items.mViews) { views.collectAllIntents(); } } } else if (action instanceof ViewGroupActionAdd vgaa && vgaa.mNestedViews != null) { vgaa.mNestedViews.collectAllIntents(); } } } } public void writeToParcel(Parcel out, int flags) { out.writeInt(mIdToUriMapping.size()); for (int i = 0; i < mIdToUriMapping.size(); i++) { out.writeInt(mIdToUriMapping.keyAt(i)); String intentUri = mIdToUriMapping.valueAt(i); out.writeString8(intentUri); RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri)) : mUriToCollectionMapping.get(intentUri); items.writeToParcel(out, flags, true); } } } private class SetRemoteViewsAdapterIntent extends Action { private class SetRemoteViewsAdapterIntent extends Action { Intent mIntent; Intent mIntent; boolean mIsAsync = false; boolean mIsAsync = false; Loading Loading @@ -3850,9 +4008,12 @@ public class RemoteViews implements Parcelable, Filter { private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { if (hierarchyRoot == null) { if (hierarchyRoot == null) { mBitmapCache = src.mBitmapCache; mBitmapCache = src.mBitmapCache; // We need to create a new instance because we don't reconstruct collection cache mCollectionCache = new RemoteCollectionCache(src.mCollectionCache); mApplicationInfoCache = src.mApplicationInfoCache; mApplicationInfoCache = src.mApplicationInfoCache; } else { } else { mBitmapCache = hierarchyRoot.mBitmapCache; mBitmapCache = hierarchyRoot.mBitmapCache; mCollectionCache = hierarchyRoot.mCollectionCache; mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; } } if (hierarchyRoot == null || src.mIsRoot) { if (hierarchyRoot == null || src.mIsRoot) { Loading Loading @@ -3926,6 +4087,7 @@ public class RemoteViews implements Parcelable, Filter { mBitmapCache = new BitmapCache(parcel); mBitmapCache = new BitmapCache(parcel); // Store the class cookies such that they are available when we clone this RemoteView. // Store the class cookies such that they are available when we clone this RemoteView. mClassCookies = parcel.copyClassCookies(); mClassCookies = parcel.copyClassCookies(); mCollectionCache = new RemoteCollectionCache(parcel); } else { } else { configureAsChild(rootData); configureAsChild(rootData); } } Loading Loading @@ -4087,6 +4249,7 @@ public class RemoteViews implements Parcelable, Filter { private void configureAsChild(@NonNull HierarchyRootData rootData) { private void configureAsChild(@NonNull HierarchyRootData rootData) { mIsRoot = false; mIsRoot = false; mBitmapCache = rootData.mBitmapCache; mBitmapCache = rootData.mBitmapCache; mCollectionCache = rootData.mRemoteCollectionCache; mApplicationInfoCache = rootData.mApplicationInfoCache; mApplicationInfoCache = rootData.mApplicationInfoCache; mClassCookies = rootData.mClassCookies; mClassCookies = rootData.mClassCookies; configureDescendantsAsChildren(); configureDescendantsAsChildren(); Loading Loading @@ -6357,6 +6520,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. // is shared by all children. if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } mApplication.writeToParcel(dest, flags); mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { if (mIsRoot || mIdealSize == null) { Loading @@ -6373,6 +6537,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } dest.writeInt(mSizedRemoteViews.size()); dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { for (RemoteViews view : mSizedRemoteViews) { Loading @@ -6384,6 +6549,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. // is shared by all children. if (mIsRoot) { if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); mBitmapCache.writeBitmapsToParcel(dest, flags); mCollectionCache.writeToParcel(dest, flags); } } mLandscape.writeToParcel(dest, flags); mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user // Both RemoteViews already share the same package and user Loading Loading @@ -7262,19 +7428,23 @@ public class RemoteViews implements Parcelable, Filter { } } private HierarchyRootData getHierarchyRootData() { private HierarchyRootData getHierarchyRootData() { return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies); return new HierarchyRootData(mBitmapCache, mCollectionCache, mApplicationInfoCache, mClassCookies); } } private static final class HierarchyRootData { private static final class HierarchyRootData { final BitmapCache mBitmapCache; final BitmapCache mBitmapCache; final RemoteCollectionCache mRemoteCollectionCache; final ApplicationInfoCache mApplicationInfoCache; final ApplicationInfoCache mApplicationInfoCache; final Map<Class, Object> mClassCookies; final Map<Class, Object> mClassCookies; HierarchyRootData( HierarchyRootData( BitmapCache bitmapCache, BitmapCache bitmapCache, RemoteCollectionCache remoteCollectionCache, ApplicationInfoCache applicationInfoCache, ApplicationInfoCache applicationInfoCache, Map<Class, Object> classCookies) { Map<Class, Object> classCookies) { mBitmapCache = bitmapCache; mBitmapCache = bitmapCache; mRemoteCollectionCache = remoteCollectionCache; mApplicationInfoCache = applicationInfoCache; mApplicationInfoCache = applicationInfoCache; mClassCookies = classCookies; mClassCookies = classCookies; } } Loading