| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.appwidget; |
| |
| import static android.appwidget.flags.Flags.remoteAdapterConversion; |
| |
| import android.annotation.BroadcastBehavior; |
| import android.annotation.FlaggedApi; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.RequiresFeature; |
| 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; |
| import android.appwidget.flags.Flags; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentSender; |
| import android.content.ServiceConnection; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ParceledListSlice; |
| 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; |
| import android.util.Log; |
| import android.widget.RemoteViews; |
| |
| import com.android.internal.appwidget.IAppWidgetService; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.util.FunctionalUtils; |
| |
| import java.util.ArrayList; |
| 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 |
| * AppWidget related state. |
| * |
| * <div class="special reference"> |
| * <h3>Developer Guides</h3> |
| * <p>For more information about creating app widgets, read the |
| * <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a> developer guide.</p> |
| * </div> |
| */ |
| @SystemService(Context.APPWIDGET_SERVICE) |
| @RequiresFeature(PackageManager.FEATURE_APP_WIDGETS) |
| public class AppWidgetManager { |
| |
| |
| /** |
| * Activity action to launch from your {@link AppWidgetHost} activity when you want to |
| * pick an AppWidget to display. The AppWidget picker activity will be launched. |
| * <p> |
| * You must supply the following extras: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_ID}</td> |
| * <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider |
| * once the user has selected one.</td> |
| * </tr> |
| * </table> |
| * |
| * <p> |
| * The system will respond with an onActivityResult call with the following extras in |
| * the intent: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_ID}</td> |
| * <td>The appWidgetId that you supplied in the original intent.</td> |
| * </tr> |
| * </table> |
| * <p> |
| * When you receive the result from the AppWidget pick activity, if the resultCode is |
| * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected. You should then |
| * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its |
| * configuration activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you |
| * should delete the appWidgetId. |
| * |
| * @see #ACTION_APPWIDGET_CONFIGURE |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK"; |
| |
| /** |
| * Similar to ACTION_APPWIDGET_PICK, but used from keyguard |
| * @hide |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String |
| ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK"; |
| |
| /** |
| * Activity action to launch from your {@link AppWidgetHost} activity when you want to bind |
| * an AppWidget to display and bindAppWidgetIdIfAllowed returns false. |
| * <p> |
| * You must supply the following extras: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_ID}</td> |
| * <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider |
| * you provide.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_PROVIDER}</td> |
| * <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget. |
| * </td> |
| * </tr> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_PROVIDER_PROFILE}</td> |
| * <td>An optional handle to a user profile under which runs the provider |
| * for this AppWidget. |
| * </td> |
| * </tr> |
| * </table> |
| * |
| * <p> |
| * The system will respond with an onActivityResult call with the following extras in |
| * the intent: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_ID}</td> |
| * <td>The appWidgetId that you supplied in the original intent.</td> |
| * </tr> |
| * </table> |
| * <p> |
| * When you receive the result from the AppWidget bind activity, if the resultCode is |
| * {@link android.app.Activity#RESULT_OK}, the AppWidget has been bound. You should then |
| * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its |
| * configuration activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you |
| * should delete the appWidgetId. |
| * |
| * @see #ACTION_APPWIDGET_CONFIGURE |
| * |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND"; |
| |
| /** |
| * Sent when it is time to configure your AppWidget while it is being added to a host. |
| * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity |
| * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo |
| * meta-data}. |
| * |
| * <p> |
| * The intent will contain the following extras: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_ID}</td> |
| * <td>The appWidgetId to configure.</td> |
| * </tr> |
| * </table> |
| * |
| * <p>If you return {@link android.app.Activity#RESULT_OK} using |
| * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added, |
| * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget. |
| * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add |
| * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} |
| * broadcast. |
| */ |
| @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) |
| public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"; |
| |
| /** |
| * An intent extra (int) that contains one appWidgetId. |
| * <p> |
| * The value will be an int that can be retrieved like this: |
| * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID} |
| */ |
| public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; |
| |
| /** |
| * A bundle extra (boolean) that contains whether or not an app has finished restoring a widget. |
| * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its |
| * widgets followed by calling {@link #updateAppWidget} to update the views. |
| * |
| * @see #updateAppWidgetOptions(int, Bundle) |
| */ |
| public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted"; |
| |
| |
| /** |
| * A bundle extra (int) that contains the lower bound on the current width, in dips, of a |
| * widget instance. |
| */ |
| public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; |
| |
| /** |
| * A bundle extra (int) that contains the lower bound on the current height, in dips, of a |
| * widget instance. |
| */ |
| public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"; |
| |
| /** |
| * A bundle extra (int) that contains the upper bound on the current width, in dips, of a |
| * widget instance. |
| */ |
| public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"; |
| |
| /** |
| * A bundle extra (int) that contains the upper bound on the current width, in dips, of a |
| * widget instance. |
| */ |
| public static final String OPTION_APPWIDGET_MAX_HEIGHT = "appWidgetMaxHeight"; |
| |
| /** |
| * A bundle extra ({@code List<SizeF>}) that contains the list of possible sizes, in dips, a |
| * widget instance can take. |
| */ |
| public static final String OPTION_APPWIDGET_SIZES = "appWidgetSizes"; |
| |
| /** |
| * A bundle extra that hints to the AppWidgetProvider the category of host that owns this |
| * this widget. Can have the value {@link |
| * AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link |
| * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD} or {@link |
| * AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}. |
| */ |
| public static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory"; |
| |
| /** |
| * An intent extra which points to a bundle of extra information for a particular widget id. |
| * In particular this bundle can contain {@link #OPTION_APPWIDGET_MIN_WIDTH}, |
| * {@link #OPTION_APPWIDGET_MIN_HEIGHT}, {@link #OPTION_APPWIDGET_MAX_WIDTH}, |
| * {@link #OPTION_APPWIDGET_MAX_HEIGHT}. |
| */ |
| public static final String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions"; |
| |
| /** |
| * An intent extra that contains multiple appWidgetIds. |
| * <p> |
| * The value will be an int array that can be retrieved like this: |
| * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS} |
| */ |
| public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds"; |
| |
| /** |
| * An intent extra that contains the component name of a AppWidget provider. |
| * <p> |
| * The value will be an {@link android.content.ComponentName}. |
| */ |
| public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider"; |
| |
| /** |
| * An intent extra that contains the user handle of the profile under |
| * which an AppWidget provider is registered. |
| * <p> |
| * The value will be a {@link android.os.UserHandle}. |
| */ |
| public static final String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile"; |
| |
| /** |
| * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of |
| * {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are |
| * installed. (This is how the launcher shows the search widget). |
| */ |
| public static final String EXTRA_CUSTOM_INFO = "customInfo"; |
| |
| /** |
| * An intent extra attached to the {@link #ACTION_APPWIDGET_HOST_RESTORED} broadcast, |
| * indicating the integer ID of the host whose widgets have just been restored. |
| */ |
| public static final String EXTRA_HOST_ID = "hostId"; |
| |
| /** |
| * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of |
| * {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are |
| * installed. It will be added to the extras object on the {@link android.content.Intent} |
| * that is returned from the picker activity. |
| * |
| * {@more} |
| */ |
| public static final String EXTRA_CUSTOM_EXTRAS = "customExtras"; |
| |
| /** |
| * An intent extra to pass to the AppWidget picker which allows the picker to filter |
| * the list based on the {@link AppWidgetProviderInfo#widgetCategory}. |
| * |
| * @hide |
| */ |
| public static final String EXTRA_CATEGORY_FILTER = "categoryFilter"; |
| |
| /** |
| * An intent extra to pass to the AppWidget picker to specify whether or not to sort |
| * the list of caller-specified extra AppWidgets along with the rest of the AppWidgets |
| * @hide |
| */ |
| public static final String EXTRA_CUSTOM_SORT = "customSort"; |
| |
| /** |
| * A sentinel value that the AppWidget manager will never return as a appWidgetId. |
| */ |
| public static final int INVALID_APPWIDGET_ID = 0; |
| |
| /** |
| * Sent when it is time to update your AppWidget. |
| * |
| * <p>This may be sent in response to a new instance for this AppWidget provider having |
| * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval} |
| * having lapsed, or the system booting. |
| * |
| * <p> |
| * The intent will contain the following extras: |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_IDS}</td> |
| * <td>The appWidgetIds to update. This may be all of the AppWidgets created for this |
| * provider, or just a subset. The system tries to send updates for as few AppWidget |
| * instances as possible.</td> |
| * </tr> |
| * </table> |
| * |
| * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE"; |
| |
| /** |
| * A combination broadcast of APPWIDGET_ENABLED and APPWIDGET_UPDATE. |
| * Sent during boot time and when the host is binding the widget for the very first time |
| * |
| * @hide |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_ENABLE_AND_UPDATE = "android.appwidget.action" |
| + ".APPWIDGET_ENABLE_AND_UPDATE"; |
| |
| /** |
| * Sent when the custom extras for an AppWidget change. |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see AppWidgetProvider#onAppWidgetOptionsChanged |
| * AppWidgetProvider.onAppWidgetOptionsChanged(Context context, |
| * AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras) |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS"; |
| |
| /** |
| * Sent when an instance of an AppWidget is deleted from its host. |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds) |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED"; |
| |
| /** |
| * Sent when the last AppWidget of this provider is removed from the last host. |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context) |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED"; |
| |
| /** |
| * Sent when an instance of an AppWidget is added to a host for the first time. |
| * This broadcast is sent at boot time if there is a AppWidgetHost installed with |
| * an instance for this provider. |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context) |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED"; |
| |
| /** |
| * Sent to an {@link AppWidgetProvider} after AppWidget state related to that provider has |
| * been restored from backup. The intent contains information about how to translate AppWidget |
| * ids from the restored data to their new equivalents. |
| * |
| * <p>The intent will contain the following extras: |
| * |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td> |
| * <td>The set of appWidgetIds represented in a restored backup that have been successfully |
| * incorporated into the current environment. This may be all of the AppWidgets known |
| * to this application, or just a subset. Each entry in this array of appWidgetIds has |
| * a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_IDS}</td> |
| * <td>The set of appWidgetIds now valid for this application. The app should look at |
| * its restored widget configuration and translate each appWidgetId in the |
| * {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding |
| * index within this array.</td> |
| * </tr> |
| * </table> |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see #ACTION_APPWIDGET_HOST_RESTORED |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_RESTORED |
| = "android.appwidget.action.APPWIDGET_RESTORED"; |
| |
| /** |
| * Sent to widget hosts after AppWidget state related to the host has been restored from |
| * backup. The intent contains information about how to translate AppWidget ids from the |
| * restored data to their new equivalents. If an application maintains multiple separate |
| * widget host instances, it will receive this broadcast separately for each one. |
| * |
| * <p>The intent will contain the following extras: |
| * |
| * <table> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td> |
| * <td>The set of appWidgetIds represented in a restored backup that have been successfully |
| * incorporated into the current environment. This may be all of the AppWidgets known |
| * to this application, or just a subset. Each entry in this array of appWidgetIds has |
| * a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link #EXTRA_APPWIDGET_IDS}</td> |
| * <td>The set of appWidgetIds now valid for this application. The app should look at |
| * its restored widget configuration and translate each appWidgetId in the |
| * {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding |
| * index within this array.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link #EXTRA_HOST_ID}</td> |
| * <td>The integer ID of the widget host instance whose state has just been restored.</td> |
| * </tr> |
| * </table> |
| * |
| * <p class="note">This is a protected intent that can only be sent |
| * by the system. |
| * |
| * @see #ACTION_APPWIDGET_RESTORED |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| @BroadcastBehavior(explicitOnly = true) |
| public static final String ACTION_APPWIDGET_HOST_RESTORED |
| = "android.appwidget.action.APPWIDGET_HOST_RESTORED"; |
| |
| 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 |
| * attached to the {@link #ACTION_APPWIDGET_RESTORED} broadcast intent. |
| * |
| * <p> |
| * The value will be an int array that can be retrieved like this: |
| * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS} |
| */ |
| public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds"; |
| |
| /** |
| * An extra that can be passed to |
| * {@link #requestPinAppWidget(ComponentName, Bundle, PendingIntent)}. This would allow the |
| * launcher app to present a custom preview to the user. |
| * |
| * <p> |
| * The value should be a {@link RemoteViews} similar to what is used with |
| * {@link #updateAppWidget} calls. |
| */ |
| public static final String EXTRA_APPWIDGET_PREVIEW = "appWidgetPreview"; |
| |
| /** |
| * Field for the manifest meta-data tag. |
| * |
| * @see AppWidgetProviderInfo |
| */ |
| public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider"; |
| |
| private final Context mContext; |
| private final String mPackageName; |
| @UnsupportedAppUsage |
| 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. |
| */ |
| public static AppWidgetManager getInstance(Context context) { |
| return (AppWidgetManager) context.getSystemService(Context.APPWIDGET_SERVICE); |
| } |
| |
| /** |
| * Creates a new instance. |
| * |
| * @param context The current context in which to operate. |
| * @param service The backing system service. |
| * @hide |
| */ |
| public AppWidgetManager(Context context, IAppWidgetService service) { |
| mContext = context; |
| mPackageName = context.getOpPackageName(); |
| mService = service; |
| mDisplayMetrics = context.getResources().getDisplayMetrics(); |
| if (mService == null) { |
| return; |
| } |
| BackgroundThread.getExecutor().execute(() -> { |
| try { |
| mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName, |
| null) |
| .stream().filter(Objects::nonNull) |
| .map(info -> info.provider).filter(p -> { |
| try { |
| Class clazz = Class.forName(p.getClassName()); |
| return AppWidgetProvider.class.isAssignableFrom(clazz); |
| } catch (Exception e) { |
| return false; |
| } |
| }).toArray(ComponentName[]::new)); |
| } catch (Exception e) { |
| Log.e(TAG, "Notify service of inheritance info", e); |
| } |
| }); |
| } |
| |
| private void tryAdapterConversion( |
| FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action, |
| RemoteViews original, String failureMsg) { |
| if (remoteAdapterConversion() |
| && (mHasPostedLegacyLists = mHasPostedLegacyLists |
| || (original != null && original.hasLegacyLists()))) { |
| final RemoteViews viewsCopy = new RemoteViews(original); |
| Runnable updateWidgetWithTask = () -> { |
| try { |
| viewsCopy.collectAllIntents().get(); |
| action.acceptOrThrow(viewsCopy); |
| } catch (Exception e) { |
| Log.e(TAG, failureMsg, e); |
| } |
| }; |
| |
| if (Looper.getMainLooper() == Looper.myLooper()) { |
| createUpdateExecutorIfNull().execute(updateWidgetWithTask); |
| return; |
| } |
| |
| updateWidgetWithTask.run(); |
| } else { |
| try { |
| action.acceptOrThrow(original); |
| } catch (RemoteException re) { |
| throw re.rethrowFromSystemServer(); |
| } |
| } |
| } |
| |
| /** |
| * Set the RemoteViews to use for the specified appWidgetIds. |
| * <p> |
| * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should |
| * contain a complete representation of the widget. For performing partial widget updates, see |
| * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}. |
| * |
| * <p> |
| * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, |
| * and outside of the handler. |
| * This method will only work when called from the uid that owns the AppWidget provider. |
| * |
| * <p> |
| * The total Bitmap memory used by the RemoteViews object cannot exceed that required to |
| * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes. |
| * |
| * @param appWidgetIds The AppWidget instances for which to set the RemoteViews. |
| * @param views The RemoteViews object to show. |
| */ |
| public void updateAppWidget(int[] appWidgetIds, RemoteViews views) { |
| if (mService == null) { |
| return; |
| } |
| |
| tryAdapterConversion(view -> mService.updateAppWidgetIds(mPackageName, appWidgetIds, |
| view), views, "Error updating app widget views in background"); |
| } |
| |
| /** |
| * Update the extras for a given widget instance. |
| * <p> |
| * The extras can be used to embed additional information about this widget to be accessed |
| * by the associated widget's AppWidgetProvider. |
| * |
| * <p> |
| * The new options are merged into existing options using {@link Bundle#putAll} semantics. |
| * |
| * @see #getAppWidgetOptions(int) |
| * |
| * @param appWidgetId The AppWidget instances for which to set the RemoteViews. |
| * @param options The options to associate with this widget |
| */ |
| public void updateAppWidgetOptions(int appWidgetId, Bundle options) { |
| if (mService == null) { |
| return; |
| } |
| try { |
| mService.updateAppWidgetOptions(mPackageName, appWidgetId, options); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the extras associated with a given widget instance. |
| * <p> |
| * The extras can be used to embed additional information about this widget to be accessed |
| * by the associated widget's AppWidgetProvider. |
| * |
| * @see #updateAppWidgetOptions(int, Bundle) |
| * |
| * @param appWidgetId The AppWidget instances for which to set the RemoteViews. |
| * @return The options associated with the given widget instance. |
| */ |
| public Bundle getAppWidgetOptions(int appWidgetId) { |
| if (mService == null) { |
| return Bundle.EMPTY; |
| } |
| try { |
| return mService.getAppWidgetOptions(mPackageName, appWidgetId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Set the RemoteViews to use for the specified appWidgetId. |
| * <p> |
| * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should |
| * contain a complete representation of the widget. For performing partial widget updates, see |
| * {@link #partiallyUpdateAppWidget(int, RemoteViews)}. |
| * |
| * <p> |
| * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, |
| * and outside of the handler. |
| * This method will only work when called from the uid that owns the AppWidget provider. |
| * |
| * <p> |
| * The total Bitmap memory used by the RemoteViews object cannot exceed that required to |
| * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes. |
| * |
| * @param appWidgetId The AppWidget instance for which to set the RemoteViews. |
| * @param views The RemoteViews object to show. |
| */ |
| public void updateAppWidget(int appWidgetId, RemoteViews views) { |
| if (mService == null) { |
| return; |
| } |
| updateAppWidget(new int[] { appWidgetId }, views); |
| } |
| |
| /** |
| * Perform an incremental update or command on the widget(s) specified by appWidgetIds. |
| * <p> |
| * This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the |
| * RemoteViews object which is passed is understood to be an incomplete representation of the |
| * widget, and hence does not replace the cached representation of the widget. As of API |
| * level 17, the new properties set within the views objects will be appended to the cached |
| * representation of the widget, and hence will persist. |
| * |
| * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)}, |
| * {@link RemoteViews#setScrollPosition(int, int)} and similar commands. |
| * |
| * <p> |
| * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, |
| * and outside of the handler. |
| * This method will only work when called from the uid that owns the AppWidget provider. |
| * |
| * <p> |
| * This method will be ignored if a widget has not received a full update via |
| * {@link #updateAppWidget(int[], RemoteViews)}. |
| * |
| * @param appWidgetIds The AppWidget instances for which to set the RemoteViews. |
| * @param views The RemoteViews object containing the incremental update / command. |
| */ |
| public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) { |
| if (mService == null) { |
| return; |
| } |
| |
| tryAdapterConversion(view -> mService.partiallyUpdateAppWidgetIds(mPackageName, |
| appWidgetIds, view), views, |
| "Error partially updating app widget views in background"); |
| } |
| |
| /** |
| * Perform an incremental update or command on the widget specified by appWidgetId. |
| * <p> |
| * This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews |
| * object which is passed is understood to be an incomplete representation of the widget, and |
| * hence is not cached by the AppWidgetService. Note that because these updates are not cached, |
| * any state that they modify that is not restored by restoreInstanceState will not persist in |
| * the case that the widgets are restored using the cached version in AppWidgetService. |
| * |
| * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)}, |
| * {@link RemoteViews#setScrollPosition(int, int)} and similar commands. |
| * |
| * <p> |
| * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, |
| * and outside of the handler. |
| * This method will only work when called from the uid that owns the AppWidget provider. |
| * |
| * <p> |
| * This method will be ignored if a widget has not received a full update via |
| * {@link #updateAppWidget(int[], RemoteViews)}. |
| * |
| * @param appWidgetId The AppWidget instance for which to set the RemoteViews. |
| * @param views The RemoteViews object containing the incremental update / command. |
| */ |
| public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) { |
| if (mService == null) { |
| return; |
| } |
| partiallyUpdateAppWidget(new int[] { appWidgetId }, views); |
| } |
| |
| /** |
| * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider. |
| * |
| * <p> |
| * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast, |
| * and outside of the handler. |
| * This method will only work when called from the uid that owns the AppWidget provider. |
| * |
| * @param provider The {@link ComponentName} for the {@link |
| * android.content.BroadcastReceiver BroadcastReceiver} provider |
| * for your AppWidget. |
| * @param views The RemoteViews object to show. |
| */ |
| public void updateAppWidget(ComponentName provider, RemoteViews views) { |
| if (mService == null) { |
| return; |
| } |
| |
| tryAdapterConversion(view -> mService.updateAppWidgetProvider(provider, view), views, |
| "Error updating app widget view using provider in background"); |
| } |
| |
| /** |
| * Updates the info for the supplied AppWidget provider. Apps can use this to change the default |
| * behavior of the widget based on the state of the app (for e.g., if the user is logged in |
| * or not). Calling this API completely replaces the previous definition. |
| * |
| * <p> |
| * The manifest entry of the provider should contain an additional meta-data tag similar to |
| * {@link #META_DATA_APPWIDGET_PROVIDER} which should point to any alternative definitions for |
| * the provider. |
| * |
| * <p> |
| * This is persisted across device reboots and app updates. If this meta-data key is not |
| * present in the manifest entry, the info reverts to default. |
| * |
| * @param provider {@link ComponentName} for the {@link |
| * android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget. |
| * @param metaDataKey key for the meta-data tag pointing to the new provider info. Use null |
| * to reset any previously set info. |
| */ |
| public void updateAppWidgetProviderInfo(ComponentName provider, @Nullable String metaDataKey) { |
| if (mService == null) { |
| return; |
| } |
| try { |
| mService.updateAppWidgetProviderInfo(provider, metaDataKey); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Notifies the specified collection view in all the specified AppWidget instances |
| * to invalidate their data. |
| * |
| * @param appWidgetIds The AppWidget instances to notify of view data changes. |
| * @param viewId The collection view id. |
| * @deprecated The corresponding API |
| * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been |
| * deprecated. Moving forward please use |
| * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)} |
| * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote |
| * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)}, |
| * {@link #updateAppWidget(int, RemoteViews)}, |
| * {@link #updateAppWidget(ComponentName, RemoteViews)}, |
| * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}, |
| * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable. |
| */ |
| @Deprecated |
| public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) { |
| if (mService == null) { |
| return; |
| } |
| |
| if (remoteAdapterConversion()) { |
| if (Looper.myLooper() == Looper.getMainLooper()) { |
| mHasPostedLegacyLists = true; |
| createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange( |
| appWidgetIds, viewId)); |
| } else { |
| notifyCollectionWidgetChange(appWidgetIds, viewId); |
| } |
| } else { |
| try { |
| mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); |
| } catch (RemoteException re) { |
| throw re.rethrowFromSystemServer(); |
| } |
| } |
| } |
| |
| private void notifyCollectionWidgetChange(int[] appWidgetIds, int viewId) { |
| try { |
| 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); |
| } |
| })); |
| } |
| CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join(); |
| } catch (Exception e) { |
| Log.e(TAG, "Error notifying changes for all widgets", e); |
| } |
| } |
| |
| /** |
| * Notifies the specified collection view in the specified AppWidget instance |
| * to invalidate its data. |
| * |
| * @param appWidgetId The AppWidget instance to notify of view data changes. |
| * @param viewId The collection view id. |
| * @deprecated The corresponding API |
| * {@link RemoteViews#setRemoteAdapter(int, Intent)} associated with this method has been |
| * deprecated. Moving forward please use |
| * {@link RemoteViews#setRemoteAdapter(int, android.widget.RemoteViews.RemoteCollectionItems)} |
| * instead to set {@link android.widget.RemoteViews.RemoteCollectionItems} for the remote |
| * adapter and update the widget views by calling {@link #updateAppWidget(int[], RemoteViews)}, |
| * {@link #updateAppWidget(int, RemoteViews)}, |
| * {@link #updateAppWidget(ComponentName, RemoteViews)}, |
| * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}, |
| * or {@link #partiallyUpdateAppWidget(int, RemoteViews)}, whichever applicable. |
| */ |
| @Deprecated |
| public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) { |
| if (mService == null) { |
| return; |
| } |
| notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId); |
| } |
| |
| /** |
| * Gets the AppWidget providers for the given user profile. User profile can only |
| * be the current user or a profile of the current user. For example, the current |
| * user may have a corporate profile. In this case the parent user profile has a |
| * child profile, the corporate one. |
| * |
| * @param profile The profile for which to get providers. Passing null is equivalent |
| * to querying for only the calling user. |
| * @return The installed providers, or an empty list if none are found for the given user. |
| * |
| * @see android.os.Process#myUserHandle() |
| * @see android.os.UserManager#getUserProfiles() |
| */ |
| public @NonNull List<AppWidgetProviderInfo> getInstalledProvidersForProfile( |
| @Nullable UserHandle profile) { |
| if (mService == null) { |
| return Collections.emptyList(); |
| } |
| return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, |
| profile, null); |
| } |
| |
| /** |
| * Gets the AppWidget providers for the given package and user profile. User |
| * profile can only be the current user or a profile of the current user. For |
| * example, the current user may have a corporate profile. In this case the |
| * parent user profile has a child profile, the corporate one. |
| * |
| * @param packageName The package for which to get providers. If null, this method is |
| * equivalent to {@link #getInstalledProvidersForProfile(UserHandle)}. |
| * @param profile The profile for which to get providers. Passing null is equivalent |
| * to querying for only the calling user. |
| * @return The installed providers, or an empty list if none are found for the given |
| * package and user. |
| * @throws NullPointerException if the provided package name is null |
| * |
| * @see android.os.Process#myUserHandle() |
| * @see android.os.UserManager#getUserProfiles() |
| */ |
| public @NonNull List<AppWidgetProviderInfo> getInstalledProvidersForPackage( |
| @NonNull String packageName, @Nullable UserHandle profile) { |
| if (packageName == null) { |
| throw new NullPointerException("A non-null package must be passed to this method. " + |
| "If you want all widgets regardless of package, see " + |
| "getInstalledProvidersForProfile(UserHandle)"); |
| } |
| if (mService == null) { |
| return Collections.emptyList(); |
| } |
| return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, |
| profile, packageName); |
| } |
| |
| /** |
| * Return a list of the AppWidget providers that are currently installed. |
| */ |
| public List<AppWidgetProviderInfo> getInstalledProviders() { |
| if (mService == null) { |
| return Collections.emptyList(); |
| } |
| return getInstalledProvidersForProfile(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN, |
| null, null); |
| } |
| |
| /** |
| * Gets the AppWidget providers for the current user. |
| * |
| * @param categoryFilter Will only return providers which register as any of the specified |
| * specified categories. See {@link AppWidgetProviderInfo#widgetCategory}. |
| * @return The intalled providers. |
| * |
| * @see android.os.Process#myUserHandle() |
| * @see android.os.UserManager#getUserProfiles() |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) { |
| if (mService == null) { |
| return Collections.emptyList(); |
| } |
| return getInstalledProvidersForProfile(categoryFilter, null, null); |
| } |
| |
| /** |
| * Gets the AppWidget providers for the given user profile. User profile can only |
| * be the current user or a profile of the current user. For example, the current |
| * user may have a corporate profile. In this case the parent user profile has a |
| * child profile, the corporate one. |
| * |
| * @param categoryFilter Will only return providers which register as any of the specified |
| * specified categories. See {@link AppWidgetProviderInfo#widgetCategory}. |
| * @param profile A profile of the current user which to be queried. The user |
| * is itself also a profile. If null, the providers only for the current user |
| * are returned. |
| * @param packageName If specified, will only return providers from the given package. |
| * @return The intalled providers. |
| * |
| * @see android.os.Process#myUserHandle() |
| * @see android.os.UserManager#getUserProfiles() |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public List<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter, |
| @Nullable UserHandle profile, @Nullable String packageName) { |
| if (mService == null) { |
| return Collections.emptyList(); |
| } |
| |
| if (profile == null) { |
| profile = mContext.getUser(); |
| } |
| |
| try { |
| ParceledListSlice<AppWidgetProviderInfo> providers = mService.getInstalledProvidersForProfile( |
| categoryFilter, profile.getIdentifier(), packageName); |
| if (providers == null) { |
| return Collections.emptyList(); |
| } |
| for (AppWidgetProviderInfo info : providers.getList()) { |
| // Converting complex to dp. |
| info.updateDimensions(mDisplayMetrics); |
| } |
| return providers.getList(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the available info about the AppWidget. |
| * |
| * @return A appWidgetId. If the appWidgetId has not been bound to a provider yet, or |
| * you don't have access to that appWidgetId, null is returned. |
| */ |
| public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) { |
| if (mService == null) { |
| Log.e(TAG, "Service wasn't initialized, appWidgetId=" + appWidgetId); |
| return null; |
| } |
| try { |
| AppWidgetProviderInfo info = mService.getAppWidgetInfo(mPackageName, appWidgetId); |
| if (info != null) { |
| // Converting complex to dp. |
| info.updateDimensions(mDisplayMetrics); |
| } else { |
| Log.e(TAG, "App widget provider info is null. PackageName=" + mPackageName |
| + " appWidgetId-" + appWidgetId); |
| } |
| return info; |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Set the component for a given appWidgetId. |
| * |
| * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding |
| * widgets always for your component. This method is used by the AppWidget picker and |
| * should not be used by other apps. |
| * |
| * @param appWidgetId The AppWidget instance for which to set the RemoteViews. |
| * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget |
| * provider for this AppWidget. |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public void bindAppWidgetId(int appWidgetId, ComponentName provider) { |
| if (mService == null) { |
| return; |
| } |
| bindAppWidgetId(appWidgetId, provider, null); |
| } |
| |
| /** |
| * Set the component for a given appWidgetId. |
| * |
| * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding |
| * widgets always for your component. This method is used by the AppWidget picker and |
| * should not be used by other apps. |
| * |
| * @param appWidgetId The AppWidget instance for which to set the RemoteViews. |
| * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget |
| * provider for this AppWidget. |
| * @param options Bundle containing options for the AppWidget. See also |
| * {@link #updateAppWidgetOptions(int, Bundle)} |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) { |
| if (mService == null) { |
| return; |
| } |
| bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUser(), provider, options); |
| } |
| |
| /** |
| * Set the component for a given appWidgetId. |
| * |
| * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE} |
| * broadcast. |
| * |
| * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding |
| * widgets always for your component. Should be used by apps that host widgets; if this |
| * method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to |
| * bind |
| * |
| * @param appWidgetId The AppWidget id under which to bind the provider. |
| * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget |
| * provider for this AppWidget. |
| * @return true if this component has permission to bind the AppWidget |
| */ |
| public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider) { |
| if (mService == null) { |
| return false; |
| } |
| return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, null); |
| } |
| |
| /** |
| * Set the component for a given appWidgetId. |
| * |
| * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE} |
| * broadcast. |
| * |
| * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding |
| * widgets always for your component. Should be used by apps that host widgets; if this |
| * method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to |
| * bind |
| * |
| * @param appWidgetId The AppWidget id under which to bind the provider. |
| * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget |
| * provider for this AppWidget. |
| * @param options Bundle containing options for the AppWidget. See also |
| * {@link #updateAppWidgetOptions(int, Bundle)} |
| * |
| * @return true if this component has permission to bind the AppWidget |
| */ |
| public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider, |
| Bundle options) { |
| if (mService == null) { |
| return false; |
| } |
| return bindAppWidgetIdIfAllowed(appWidgetId, mContext.getUserId(), provider, options); |
| } |
| |
| /** |
| * Set the provider for a given appWidgetId if the caller has a permission. |
| * |
| * If successful, the app widget provider will receive a {@link #ACTION_APPWIDGET_UPDATE} |
| * broadcast. |
| * |
| * <p> |
| * <strong>Note:</strong> You need the {@link android.Manifest.permission#BIND_APPWIDGET} |
| * permission or the user must have enabled binding widgets always for your component. |
| * Should be used by apps that host widgets. If this method returns false, call {@link |
| * #ACTION_APPWIDGET_BIND} to request permission to bind. |
| * </p> |
| * |
| * @param appWidgetId The AppWidget id under which to bind the provider. |
| * @param user The user id in which the provider resides. |
| * @param provider The component name of the provider. |
| * @param options An optional Bundle containing options for the AppWidget. |
| * |
| * @return true if this component has permission to bind the AppWidget |
| */ |
| public boolean bindAppWidgetIdIfAllowed(int appWidgetId, UserHandle user, |
| ComponentName provider, Bundle options) { |
| if (mService == null) { |
| return false; |
| } |
| return bindAppWidgetIdIfAllowed(appWidgetId, user.getIdentifier(), provider, options); |
| } |
| |
| /** |
| * Query if a given package was granted permission by the user to bind app widgets |
| * |
| * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission |
| * |
| * @param packageName The package for which the permission is being queried |
| * @param userId The user id of the user under which the package runs. |
| * @return true if the package was granted permission by the user to bind app widgets |
| * @hide |
| */ |
| public boolean hasBindAppWidgetPermission(String packageName, int userId) { |
| if (mService == null) { |
| return false; |
| } |
| try { |
| return mService.hasBindAppWidgetPermission(packageName, userId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Query if a given package was granted permission by the user to bind app widgets |
| * |
| * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission |
| * |
| * @param packageName The package for which the permission is being queried |
| * @return true if the package was granted permission by the user to bind app widgets |
| * @hide |
| */ |
| public boolean hasBindAppWidgetPermission(String packageName) { |
| if (mService == null) { |
| return false; |
| } |
| try { |
| return mService.hasBindAppWidgetPermission(packageName, mContext.getUserId()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Changes any user-granted permission for the given package to bind app widgets |
| * |
| * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission |
| * |
| * @param packageName The package whose permission is being changed |
| * @param permission Whether to give the package permission to bind widgets |
| * |
| * @hide |
| */ |
| public void setBindAppWidgetPermission(String packageName, boolean permission) { |
| if (mService == null) { |
| return; |
| } |
| setBindAppWidgetPermission(packageName, mContext.getUserId(), permission); |
| } |
| |
| /** |
| * Changes any user-granted permission for the given package to bind app widgets |
| * |
| * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission |
| * |
| * @param packageName The package whose permission is being changed |
| * @param userId The user under which the package is running. |
| * @param permission Whether to give the package permission to bind widgets |
| * |
| * @hide |
| */ |
| @TestApi |
| public void setBindAppWidgetPermission( |
| @NonNull String packageName, @UserIdInt int userId, boolean permission) { |
| if (mService == null) { |
| return; |
| } |
| try { |
| mService.setBindAppWidgetPermission(packageName, userId, permission); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Binds the RemoteViewsService for a given appWidgetId and intent. |
| * |
| * The appWidgetId specified must already be bound to the calling AppWidgetHost via |
| * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}. |
| * |
| * @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService. |
| * @param intent The intent of the service which will be providing the data to the |
| * RemoteViewsAdapter. |
| * @param connection The callback interface to be notified when a connection is made or lost. |
| * @param flags Flags used for binding to the service. Currently only |
| * {@link Context#BIND_AUTO_CREATE} and |
| * {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported. |
| * |
| * @see Context#getServiceDispatcher(ServiceConnection, Handler, long) |
| * @hide |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public boolean bindRemoteViewsService(Context context, int appWidgetId, Intent intent, |
| IServiceConnection connection, @Context.BindServiceFlagsBits int flags) { |
| if (mService == null) { |
| return false; |
| } |
| try { |
| return mService.bindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent, |
| context.getIApplicationThread(), context.getActivityToken(), connection, |
| Integer.toUnsignedLong(flags)); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the list of appWidgetIds that have been bound to the given AppWidget |
| * provider. |
| * |
| * @param provider The {@link android.content.BroadcastReceiver} that is the |
| * AppWidget provider to find appWidgetIds for. |
| */ |
| public int[] getAppWidgetIds(ComponentName provider) { |
| if (mService == null) { |
| return new int[0]; |
| } |
| try { |
| return mService.getAppWidgetIds(provider); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| public boolean isBoundWidgetPackage(String packageName, int userId) { |
| if (mService == null) { |
| return false; |
| } |
| try { |
| return mService.isBoundWidgetPackage(packageName, userId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId, |
| ComponentName provider, Bundle options) { |
| if (mService == null) { |
| return false; |
| } |
| try { |
| return mService.bindAppWidgetId(mPackageName, appWidgetId, |
| profileId, provider, options); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Return {@code TRUE} if the default launcher supports |
| * {@link #requestPinAppWidget(ComponentName, Bundle, PendingIntent)} |
| */ |
| public boolean isRequestPinAppWidgetSupported() { |
| try { |
| return mService.isRequestPinAppWidgetSupported(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Only used during development. Can be deleted before release. |
| * @hide |
| */ |
| public boolean requestPinAppWidget(@NonNull ComponentName provider, |
| @Nullable PendingIntent successCallback) { |
| return requestPinAppWidget(provider, null, successCallback); |
| } |
| |
| /** |
| * Request to pin an app widget on the current launcher. It's up to the launcher to accept this |
| * request (optionally showing a user confirmation). If the request is accepted, the caller will |
| * get a confirmation with extra {@link #EXTRA_APPWIDGET_ID}. |
| * |
| * <p>When a request is denied by the user, the caller app will not get any response. |
| * |
| * <p>Only apps with a foreground activity or a foreground service can call it. Otherwise |
| * it'll throw {@link IllegalStateException}. |
| * |
| * <p>It's up to the launcher how to handle previous pending requests when the same package |
| * calls this API multiple times in a row. It may ignore the previous requests, |
| * for example. |
| * |
| * <p>Launcher will not show the configuration activity associated with the provider in this |
| * case. The app could either show the configuration activity as a response to the callback, |
| * or show if before calling the API (various configurations can be encapsulated in |
| * {@code successCallback} to avoid persisting them before the widgetId is known). |
| * |
| * @param provider The {@link ComponentName} for the {@link |
| * android.content.BroadcastReceiver BroadcastReceiver} provider for your AppWidget. |
| * @param extras In not null, this is passed to the launcher app. For eg {@link |
| * #EXTRA_APPWIDGET_PREVIEW} can be used for a custom preview. |
| * @param successCallback If not null, this intent will be sent when the widget is created. |
| * |
| * @return {@code TRUE} if the launcher supports this feature. Note the API will return without |
| * waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean |
| * the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature or if |
| * calling app belongs to a user-profile with items restricted on home screen. |
| * |
| * @see android.content.pm.ShortcutManager#isRequestPinShortcutSupported() |
| * @see android.content.pm.ShortcutManager#requestPinShortcut(ShortcutInfo, IntentSender) |
| * @see #isRequestPinAppWidgetSupported() |
| * |
| * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground |
| * service or when the user is locked. |
| */ |
| public boolean requestPinAppWidget(@NonNull ComponentName provider, |
| @Nullable Bundle extras, @Nullable PendingIntent successCallback) { |
| try { |
| return mService.requestPinAppWidget(mPackageName, provider, extras, |
| successCallback == null ? null : successCallback.getIntentSender()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Note an app widget is tapped on. |
| * |
| * @param appWidgetId App widget id. |
| * @hide |
| */ |
| public void noteAppWidgetTapped(int appWidgetId) { |
| try { |
| mService.noteAppWidgetTapped(mPackageName, appWidgetId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Set a preview for this widget. This preview will be used instead of the provider's {@link |
| * AppWidgetProviderInfo#previewLayout previewLayout} or {@link |
| * AppWidgetProviderInfo#previewImage previewImage} for previewing the widget in the widget |
| * picker and pin app widget flow. |
| * |
| * @param provider The {@link ComponentName} for the {@link android.content.BroadcastReceiver |
| * BroadcastReceiver} provider for the AppWidget you intend to provide a preview for. |
| * @param widgetCategories The categories that this preview should be used for. This can be a |
| * single category or combination of categories. If multiple categories are specified, |
| * then this preview will be used for each of those categories. For example, if you |
| * set a preview for WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD, the preview will |
| * be used when picking widgets for the home screen and keyguard. |
| * |
| * <p>Note: You should only use the widget categories that the provider supports, as defined |
| * in {@link AppWidgetProviderInfo#widgetCategory}. |
| * @param preview This preview will be used for previewing the provider when picking widgets for |
| * the selected categories. |
| * |
| * @see AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN |
| * @see AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD |
| * @see AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX |
| * |
| * @return true if the call was successful, false if it was rate-limited. |
| */ |
| @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS) |
| public boolean setWidgetPreview(@NonNull ComponentName provider, |
| @AppWidgetProviderInfo.CategoryFlags int widgetCategories, |
| @NonNull RemoteViews preview) { |
| try { |
| return mService.setWidgetPreview(provider, widgetCategories, preview); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Get the RemoteViews previews for this widget. |
| * |
| * @param provider The {@link ComponentName} for the {@link android.content.BroadcastReceiver |
| * BroadcastReceiver} provider for the AppWidget you intend to get a preview for. |
| * @param profile The profile in which the provider resides. Passing null is equivalent |
| * to querying for only the calling user. |
| * @param widgetCategory The widget category for which you want to display previews. This should |
| * be a single category. If a combination of categories is provided, this function will |
| * return a preview that matches at least one of the categories. |
| * |
| * @return The widget preview for the selected category, if available. |
| * @see AppWidgetProviderInfo#generatedPreviewCategories |
| */ |
| @Nullable |
| @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS) |
| public RemoteViews getWidgetPreview(@NonNull ComponentName provider, |
| @Nullable UserHandle profile, @AppWidgetProviderInfo.CategoryFlags int widgetCategory) { |
| try { |
| if (profile == null) { |
| profile = mContext.getUser(); |
| } |
| return mService.getWidgetPreview(mPackageName, provider, profile.getIdentifier(), |
| widgetCategory); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Remove this provider's preview for the specified widget categories. If the provider does not |
| * have a preview for the specified widget category, this is a no-op. |
| * |
| * @param provider The AppWidgetProvider to remove previews for. |
| * @param widgetCategories The categories of the preview to remove. For example, removing the |
| * preview for WIDGET_CATEGORY_HOME_SCREEN | WIDGET_CATEGORY_KEYGUARD will remove the |
| * previews for both categories. |
| */ |
| @FlaggedApi(Flags.FLAG_GENERATED_PREVIEWS) |
| public void removeWidgetPreview(@NonNull ComponentName provider, |
| @AppWidgetProviderInfo.CategoryFlags int widgetCategories) { |
| try { |
| mService.removeWidgetPreview(provider, widgetCategories); |
| } catch (RemoteException e) { |
| 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(); |
| } |
| } |