From 093612776337eb9f68a177fd637d1302128c3f15 Mon Sep 17 00:00:00 2001 From: Sihua Ma Date: Tue, 18 Jul 2023 20:44:44 +0000 Subject: [PATCH] Notify the change in collection items by passing the old RemoteViews Also tuning the meta parameter a bit to mitigate the parcel exception Bug: 245950570 Test: Manual Change-Id: I1c34e8cf1b30f8a24dddafeed0eec50415457d0b --- .../android/appwidget/AppWidgetManager.java | 22 +++++++- core/java/android/widget/RemoteViews.java | 55 ++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index eb672dcc42fd9..b159321af837b 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -48,9 +48,11 @@ import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.CompletableFuture; /** * Updates AppWidget state; gets information about installed AppWidget providers and other @@ -785,7 +787,25 @@ public class AppWidgetManager { return; } try { - mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); + if (RemoteViews.isAdapterConversionEnabled()) { + List> updateFutures = new ArrayList<>(); + for (int i = 0; i < appWidgetIds.length; i++) { + final int widgetId = appWidgetIds[i]; + updateFutures.add(CompletableFuture.runAsync(() -> { + try { + RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId); + if (views.replaceRemoteCollections(viewId)) { + updateAppWidget(widgetId, views); + } + } catch (Exception e) { + Log.e(TAG, "Error notifying changes in RemoteViews", e); + } + })); + } + CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join(); + } else { + mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index d9e76fefad7fe..a2f95fa9df45b 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -786,6 +786,42 @@ public class RemoteViews implements Parcelable, Filter { } } + /** + * @hide + * @return True if there is a change + */ + public boolean replaceRemoteCollections(int viewId) { + boolean isActionReplaced = false; + if (mActions != null) { + for (int i = 0; i < mActions.size(); i++) { + Action action = mActions.get(i); + if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction + && itemsAction.viewId == viewId + && itemsAction.mServiceIntent != null) { + mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId, + itemsAction.mServiceIntent)); + isActionReplaced = true; + } else if (action instanceof ViewGroupActionAdd groupAction + && groupAction.mNestedViews != null) { + isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId); + } + } + } + if (mSizedRemoteViews != null) { + for (int i = 0; i < mSizedRemoteViews.size(); i++) { + isActionReplaced |= mSizedRemoteViews.get(i).replaceRemoteCollections(viewId); + } + } + if (mLandscape != null) { + isActionReplaced |= mLandscape.replaceRemoteCollections(viewId); + } + if (mPortrait != null) { + isActionReplaced |= mPortrait.replaceRemoteCollections(viewId); + } + + return isActionReplaced; + } + private static void visitIconUri(Icon icon, @NonNull Consumer visitor) { if (icon != null && (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { @@ -1059,18 +1095,21 @@ public class RemoteViews implements Parcelable, Filter { private class SetRemoteCollectionItemListAdapterAction extends Action { private @NonNull CompletableFuture mItemsFuture; + final Intent mServiceIntent; SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { viewId = id; items.setHierarchyRootData(getHierarchyRootData()); mItemsFuture = CompletableFuture.completedFuture(items); + mServiceIntent = null; } SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) { viewId = id; mItemsFuture = getItemsFutureFromIntentWithTimeout(intent); setHierarchyRootData(getHierarchyRootData()); + mServiceIntent = intent; } private static CompletableFuture getItemsFutureFromIntentWithTimeout( @@ -1119,6 +1158,7 @@ public class RemoteViews implements Parcelable, Filter { viewId = parcel.readInt(); mItemsFuture = CompletableFuture.completedFuture( new RemoteCollectionItems(parcel, getHierarchyRootData())); + mServiceIntent = parcel.readTypedObject(Intent.CREATOR); } @Override @@ -1148,6 +1188,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(viewId); RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); items.writeToParcel(dest, flags, /* attached= */ true); + dest.writeTypedObject(mServiceIntent, flags); } @Override @@ -4765,15 +4806,23 @@ public class RemoteViews implements Parcelable, Filter { * providing data to the RemoteViewsAdapter */ public void setRemoteAdapter(@IdRes int viewId, Intent intent) { - if (AppGlobals.getIntCoreSetting( - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1) { + if (isAdapterConversionEnabled()) { addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent)); return; } addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); } + /** + * @hide + * @return True if the remote adapter conversion is enabled + */ + public static boolean isAdapterConversionEnabled() { + return AppGlobals.getIntCoreSetting( + SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, + SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1; + } + /** * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView, * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.