/* * 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. * *
For more information about creating app widgets, read the * App Widgets developer guide.
** You must supply the following extras: *
{@link #EXTRA_APPWIDGET_ID} | *A newly allocated appWidgetId, which will be bound to the AppWidget provider * once the user has selected one. | *
* The system will respond with an onActivityResult call with the following extras in * the intent: *
{@link #EXTRA_APPWIDGET_ID} | *The appWidgetId that you supplied in the original intent. | *
* 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. *
* You must supply the following extras: *
{@link #EXTRA_APPWIDGET_ID} | *A newly allocated appWidgetId, which will be bound to the AppWidget provider * you provide. | *
{@link #EXTRA_APPWIDGET_PROVIDER} | *The BroadcastReceiver that will be the AppWidget provider for this AppWidget. * | *
{@link #EXTRA_APPWIDGET_PROVIDER_PROFILE} | *An optional handle to a user profile under which runs the provider * for this AppWidget. * | *
* The system will respond with an onActivityResult call with the following extras in * the intent: *
{@link #EXTRA_APPWIDGET_ID} | *The appWidgetId that you supplied in the original intent. | *
* 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}. * *
* The intent will contain the following extras: *
{@link #EXTRA_APPWIDGET_ID} | *The appWidgetId to configure. | *
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. *
* 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. *
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
* 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.
*
* 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.
*
* 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.
*
* 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.
*
*
* The intent will contain the following extras:
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* The intent will contain the following extras:
*
* 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.
*
* The intent will contain the following extras:
*
* 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.
*
*
* 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.
*
*
* 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
* 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)}.
*
*
* 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.
*
*
* 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.
*
* The extras can be used to embed additional information about this widget to be accessed
* by the associated widget's AppWidgetProvider.
*
*
* 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.
*
* 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.
*
* 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)}.
*
*
* 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.
*
*
* 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.
*
* 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.
*
*
* 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.
*
*
* 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.
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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 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.
*
* 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.
*
* 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.
*
* 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.
*
*
* Note: 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.
* 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
*
* 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
*
* 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
*
* 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}.
*
* When a request is denied by the user, the caller app will not get any response.
*
* Only apps with a foreground activity or a foreground service can call it. Otherwise
* it'll throw {@link IllegalStateException}.
*
* 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.
*
* 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.
*
* 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();
}
}
*
*
* @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.
*
*
*
* {@link #EXTRA_APPWIDGET_IDS}
* 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.
*
*
*
*
*
* {@link #EXTRA_APPWIDGET_OLD_IDS}
* 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.
*
*
* {@link #EXTRA_APPWIDGET_IDS}
* 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.
*
*
*
*
*
* {@link #EXTRA_APPWIDGET_OLD_IDS}
* 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.
*
*
* {@link #EXTRA_APPWIDGET_IDS}
* 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.
*
*
* {@link #EXTRA_HOST_ID}
* The integer ID of the widget host instance whose state has just been restored.
*