diff options
| author | 2023-08-08 17:40:37 +0000 | |
|---|---|---|
| committer | 2023-08-22 21:04:36 +0000 | |
| commit | 385b6196e7eb766557d259ae8592ac7ec6c088cb (patch) | |
| tree | 95d34e18236abdf29a62f324290339695bb5dc61 | |
| parent | 770f721742d0dd406b6922c3539cd043a25c06a2 (diff) | |
Update RemoteViews in background
This helps ensure that the client doesn't block when calling expensive
updating functions in AppWidgetManager
Test: Manual
Bug: 245950570
Change-Id: Ia90d8e276567d5f7156c53c3d32be80420b4cff1
| -rw-r--r-- | core/java/android/appwidget/AppWidgetManager.java | 127 | ||||
| -rw-r--r-- | core/java/android/widget/RemoteViews.java | 41 |
2 files changed, 149 insertions, 19 deletions
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index b159321af837..85d669ed7907 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -24,6 +24,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.IServiceConnection; import android.app.PendingIntent; @@ -39,6 +40,10 @@ import android.content.pm.ShortcutInfo; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.util.DisplayMetrics; @@ -53,6 +58,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; /** * Updates AppWidget state; gets information about installed AppWidget providers and other @@ -475,6 +481,8 @@ public class AppWidgetManager { private static final String TAG = "AppWidgetManager"; + private static Executor sUpdateExecutor; + /** * An intent extra that contains multiple appWidgetIds. These are id values as * they were provided to the application during a recent restore from backup. It is @@ -510,6 +518,8 @@ public class AppWidgetManager { private final IAppWidgetService mService; private final DisplayMetrics mDisplayMetrics; + private boolean mHasPostedLegacyLists = false; + /** * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context * Context} object. @@ -552,6 +562,13 @@ 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. * <p> @@ -575,6 +592,19 @@ public class AppWidgetManager { if (mService == null) { return; } + + if (isPostingTaskToBackground(views)) { + createUpdateExecutorIfNull().execute(() -> { + try { + mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); + } catch (RemoteException e) { + Log.e(TAG, "Error updating app widget views in background", e); + } + }); + + return; + } + try { mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); } catch (RemoteException e) { @@ -683,6 +713,19 @@ public class AppWidgetManager { if (mService == null) { return; } + + if (isPostingTaskToBackground(views)) { + createUpdateExecutorIfNull().execute(() -> { + try { + mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); + } catch (RemoteException e) { + Log.e(TAG, "Error partially updating app widget views in background", e); + } + }); + + return; + } + try { mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); } catch (RemoteException e) { @@ -738,6 +781,19 @@ public class AppWidgetManager { if (mService == null) { return; } + + if (isPostingTaskToBackground(views)) { + createUpdateExecutorIfNull().execute(() -> { + try { + mService.updateAppWidgetProvider(provider, views); + } catch (RemoteException e) { + Log.e(TAG, "Error updating app widget view using provider in background", e); + } + }); + + return; + } + try { mService.updateAppWidgetProvider(provider, views); } catch (RemoteException e) { @@ -786,28 +842,45 @@ public class AppWidgetManager { if (mService == null) { return; } + + if (!RemoteViews.isAdapterConversionEnabled()) { + try { + mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + + return; + } + + if (Looper.myLooper() == Looper.getMainLooper()) { + mHasPostedLegacyLists = true; + createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds, + viewId)); + } else { + notifyCollectionWidgetChange(appWidgetIds, viewId); + } + } + + private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) { try { - if (RemoteViews.isAdapterConversionEnabled()) { - List<CompletableFuture<Void>> 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); + List<CompletableFuture<Void>> 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); } - })); - } - CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join(); - } else { - mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); + } catch (Exception e) { + Log.e(TAG, "Error notifying changes in RemoteViews", e); + } + })); } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join(); + } catch (Exception e) { + Log.e(TAG, "Error notifying changes for all widgets", e); } } @@ -1338,4 +1411,20 @@ public class AppWidgetManager { throw e.rethrowFromSystemServer(); } } + + @UiThread + private static @NonNull Executor createUpdateExecutorIfNull() { + if (sUpdateExecutor == null) { + sUpdateExecutor = new HandlerExecutor(createAndStartNewHandler( + "widget_manager_update_helper_thread", Process.THREAD_PRIORITY_FOREGROUND)); + } + + return sUpdateExecutor; + } + + private static @NonNull Handler createAndStartNewHandler(@NonNull String name, int priority) { + HandlerThread thread = new HandlerThread(name, priority); + thread.start(); + return thread.getThreadHandler(); + } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index fea3b78f27b4..aa8aac1c174f 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -801,6 +801,11 @@ public class RemoteViews implements Parcelable, Filter { mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId, itemsAction.mServiceIntent)); isActionReplaced = true; + } else if (action instanceof SetRemoteViewsAdapterIntent intentAction + && intentAction.viewId == viewId) { + mActions.set(i, new SetRemoteCollectionItemListAdapterAction( + intentAction.viewId, intentAction.intent)); + isActionReplaced = true; } else if (action instanceof ViewGroupActionAdd groupAction && groupAction.mNestedViews != null) { isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId); @@ -822,6 +827,42 @@ public class RemoteViews implements Parcelable, Filter { return isActionReplaced; } + /** + * @return True if has set remote adapter using service intent + * @hide + */ + public boolean hasLegacyLists() { + if (mActions != null) { + for (int i = 0; i < mActions.size(); i++) { + Action action = mActions.get(i); + if ((action instanceof SetRemoteCollectionItemListAdapterAction itemsAction + && itemsAction.mServiceIntent != null) + || (action instanceof SetRemoteViewsAdapterIntent intentAction + && intentAction.intent != null) + || (action instanceof ViewGroupActionAdd groupAction + && groupAction.mNestedViews != null + && groupAction.mNestedViews.hasLegacyLists())) { + return true; + } + } + } + if (mSizedRemoteViews != null) { + for (int i = 0; i < mSizedRemoteViews.size(); i++) { + if (mSizedRemoteViews.get(i).hasLegacyLists()) { + return true; + } + } + } + if (mLandscape != null && mLandscape.hasLegacyLists()) { + return true; + } + if (mPortrait != null && mPortrait.hasLegacyLists()) { + return true; + } + + return false; + } + private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) { if (icon != null && (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { |