diff options
| author | 2023-07-13 22:03:23 +0000 | |
|---|---|---|
| committer | 2023-07-13 22:03:23 +0000 | |
| commit | 2500204620dd6d8321b8a893a46e459741cf12a4 (patch) | |
| tree | f09bccdc0a379e8fb69b9fef7669c8fa440fc55c | |
| parent | 78aa34401fa8aab053275f2ccea65bf2a8d89da7 (diff) | |
| parent | 24c3cc53af1d059da0ec36e33508adcac9051b53 (diff) | |
Merge "Deprecating RemoteViewsAdapter in RemoteViews" into udc-qpr-dev
6 files changed, 163 insertions, 10 deletions
diff --git a/core/java/android/widget/RemoteViews.aidl b/core/java/android/widget/RemoteViews.aidl index ec86410cf89c..6a5fc03fcc6e 100644 --- a/core/java/android/widget/RemoteViews.aidl +++ b/core/java/android/widget/RemoteViews.aidl @@ -17,3 +17,4 @@ package android.widget; parcelable RemoteViews; +parcelable RemoteViews.RemoteCollectionItems; diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index bd7f5a0924cc..d9e76fefad7f 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -33,16 +33,19 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityOptions; import android.app.ActivityThread; +import android.app.AppGlobals; import android.app.Application; import android.app.LoadedApk; import android.app.PendingIntent; import android.app.RemoteInput; import android.appwidget.AppWidgetHostView; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentSender; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.ColorStateList; @@ -65,10 +68,12 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; +import android.os.RemoteException; import android.os.StrictMode; import android.os.UserHandle; import android.system.Os; @@ -98,8 +103,10 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.R; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.Preconditions; +import com.android.internal.widget.IRemoteViewsFactory; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -124,7 +131,9 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Stack; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; @@ -324,6 +333,13 @@ public class RemoteViews implements Parcelable, Filter { (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class); /** + * The maximum waiting time for remote adapter conversion in milliseconds + * + * @hide + */ + private static final int MAX_ADAPTER_CONVERSION_WAITING_TIME_MS = 2000; + + /** * Application that hosts the remote views. * * @hide @@ -1042,28 +1058,96 @@ public class RemoteViews implements Parcelable, Filter { } private class SetRemoteCollectionItemListAdapterAction extends Action { - private final RemoteCollectionItems mItems; + private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture; - SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) { + SetRemoteCollectionItemListAdapterAction(@IdRes int id, + @NonNull RemoteCollectionItems items) { viewId = id; - mItems = items; - mItems.setHierarchyRootData(getHierarchyRootData()); + items.setHierarchyRootData(getHierarchyRootData()); + mItemsFuture = CompletableFuture.completedFuture(items); + } + + SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) { + viewId = id; + mItemsFuture = getItemsFutureFromIntentWithTimeout(intent); + setHierarchyRootData(getHierarchyRootData()); + } + + private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout( + Intent intent) { + if (intent == null) { + Log.e(LOG_TAG, "Null intent received when generating adapter future"); + return CompletableFuture.completedFuture(new RemoteCollectionItems + .Builder().build()); + } + + final Context context = ActivityThread.currentApplication(); + final CompletableFuture<RemoteCollectionItems> result = new CompletableFuture<>(); + + context.bindService(intent, Context.BindServiceFlags.of(Context.BIND_AUTO_CREATE), + result.defaultExecutor(), new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, + IBinder iBinder) { + RemoteCollectionItems items; + try { + items = IRemoteViewsFactory.Stub.asInterface(iBinder) + .getRemoteCollectionItems(); + } catch (RemoteException re) { + items = new RemoteCollectionItems.Builder().build(); + Log.e(LOG_TAG, "Error getting collection items from the factory", + re); + } finally { + context.unbindService(this); + } + + result.complete(items); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { } + }); + + result.completeOnTimeout( + new RemoteCollectionItems.Builder().build(), + MAX_ADAPTER_CONVERSION_WAITING_TIME_MS, TimeUnit.MILLISECONDS); + + return result; } SetRemoteCollectionItemListAdapterAction(Parcel parcel) { viewId = parcel.readInt(); - mItems = new RemoteCollectionItems(parcel, getHierarchyRootData()); + mItemsFuture = CompletableFuture.completedFuture( + new RemoteCollectionItems(parcel, getHierarchyRootData())); } @Override public void setHierarchyRootData(HierarchyRootData rootData) { - mItems.setHierarchyRootData(rootData); + mItemsFuture = mItemsFuture + .thenApply(rc -> { + rc.setHierarchyRootData(rootData); + return rc; + }); + } + + 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; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); - mItems.writeToParcel(dest, flags, /* attached= */ true); + RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + items.writeToParcel(dest, flags, /* attached= */ true); } @Override @@ -1072,6 +1156,8 @@ public class RemoteViews implements Parcelable, Filter { View target = root.findViewById(viewId); if (target == null) return; + RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { Log.e(LOG_TAG, "setRemoteAdapter can only be used for " @@ -1092,10 +1178,10 @@ public class RemoteViews implements Parcelable, Filter { // recycling in setAdapter, so we must call setAdapter again if the number of view types // increases. if (adapter instanceof RemoteCollectionItemsAdapter - && adapter.getViewTypeCount() >= mItems.getViewTypeCount()) { + && adapter.getViewTypeCount() >= items.getViewTypeCount()) { try { ((RemoteCollectionItemsAdapter) adapter).setData( - mItems, params.handler, params.colorResources); + items, params.handler, params.colorResources); } catch (Throwable throwable) { // setData should never failed with the validation in the items builder, but if // it does, catch and rethrow. @@ -1105,7 +1191,7 @@ public class RemoteViews implements Parcelable, Filter { } try { - adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems, + adapterView.setAdapter(new RemoteCollectionItemsAdapter(items, params.handler, params.colorResources)); } catch (Throwable throwable) { // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to @@ -4679,6 +4765,12 @@ 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) { + addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent)); + return; + } addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); } diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index 214e5cc01b9e..d4f4d19e9bad 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -43,6 +43,13 @@ public abstract class RemoteViewsService extends Service { private static final Object sLock = new Object(); /** + * Used for determining the maximum number of entries to retrieve from RemoteViewsFactory + * + * @hide + */ + private static final int MAX_NUM_ENTRY = 25; + + /** * An interface for an adapter between a remote collection view (ListView, GridView, etc) and * the underlying data for that view. The implementor is responsible for making a RemoteView * for each item in the data set. This interface is a thin wrapper around {@link Adapter}. @@ -227,6 +234,30 @@ public abstract class RemoteViewsService extends Service { } } + @Override + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems + .Builder().build(); + + try { + RemoteViews.RemoteCollectionItems.Builder itemsBuilder = + new RemoteViews.RemoteCollectionItems.Builder(); + mFactory.onDataSetChanged(); + + itemsBuilder.setHasStableIds(mFactory.hasStableIds()); + final int numOfEntries = Math.min(mFactory.getCount(), MAX_NUM_ENTRY); + for (int i = 0; i < numOfEntries; i++) { + itemsBuilder.addItem(mFactory.getItemId(i), mFactory.getViewAt(i)); + } + + items = itemsBuilder.build(); + } catch (Exception ex) { + Thread t = Thread.currentThread(); + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex); + } + return items; + } + private RemoteViewsFactory mFactory; private boolean mIsCreated; } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 9ffccb34f44d..0ba271f253fb 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -532,6 +532,17 @@ public final class SystemUiDeviceConfigFlags { public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot"; /** + * (boolean) Whether to enable the adapter conversion in RemoteViews + */ + public static final String REMOTEVIEWS_ADAPTER_CONVERSION = "remoteviews_adapter_conversion"; + + /** + * Default value for whether the adapter conversion is enabled or not. This is set for + * RemoteViews and should not be a common practice. + */ + public static final boolean REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT = false; + + /** * (boolean) Whether the task manager should show a stop button if the app is allowlisted * by the user. */ diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl index c06dab9f75d6..918d9c029ef5 100644 --- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl @@ -39,5 +39,6 @@ interface IRemoteViewsFactory { boolean hasStableIds(); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isCreated(); + RemoteViews.RemoteCollectionItems getRemoteCollectionItems(); } diff --git a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java index 184b9eac24f3..4f722cefcf9f 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsAdapterTest.java @@ -353,6 +353,23 @@ public class RemoteViewsAdapterTest { public boolean isCreated() { return false; } + + @Override + public RemoteViews.RemoteCollectionItems getRemoteCollectionItems() { + RemoteViews.RemoteCollectionItems.Builder itemsBuilder = + new RemoteViews.RemoteCollectionItems.Builder(); + itemsBuilder.setHasStableIds(hasStableIds()) + .setViewTypeCount(getViewTypeCount()); + try { + for (int i = 0; i < mCount; i++) { + itemsBuilder.addItem(getItemId(i), getViewAt(i)); + } + } catch (RemoteException e) { + // No-op + } + + return itemsBuilder.build(); + } } private static class DistinctIntent extends Intent { |