page.title=App Widget Host page.tags="AppWidgetHost","home screen","launcher" @jd:body
The Android Home screen available on most Android devices allows the user to embed app widgets for quick access to content. If you're building a Home replacement or a similar app, you can also allow the user to embed app widgets by implementing an {@link android.appwidget.AppWidgetHost}. This is not something that most apps will ever need to do, but if you are creating your own host, it's important to understand the contractual obligations a host implicitly agrees to.
This document focuses on the responsibilities involved in implementing a custom {@link android.appwidget.AppWidgetHost}. For an example of how to implement an {@link android.appwidget.AppWidgetHost}, see the source code for the Android Home screen Launcher.
Here is an overview of key classes and concepts involved in implementing a custom {@link android.appwidget.AppWidgetHost}:
When a user adds an app widget to a host, a process called binding occurs. Binding refers to associating a particular app widget ID to a specific host and to a specific {@link android.appwidget.AppWidgetProvider}. There are different ways of achieving this, depending on what version of Android your app is running on.
On devices running Android version 4.0 and lower, users add app widgets via a system activity that allows users to select a widget. This implicitly does a permission check—that is, by adding the app widget, the user is implicitly granting permission to your app to add app widgets to the host. Here is an example that illustrates this approach, taken from the original Launcher. In this snippet, an event handler invokes {@link android.app.Activity#startActivityForResult(android.content.Intent,int) startActivityForResult()} with the request code {@code REQUEST_PICK_APPWIDGET} in response to a user action:
private static final int REQUEST_CREATE_APPWIDGET = 5; private static final int REQUEST_PICK_APPWIDGET = 9; ... public void onClick(DialogInterface dialog, int which) { switch (which) { ... case AddAdapter.ITEM_APPWIDGET: { ... int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); pickIntent.putExtra (AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); ... startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET); break; } ... }
When the system activity finishes, it returns a result with the user's chosen app widget to your activity. In the following example, the activity responds by calling {@code addAppWidget()} to add the app widget:
public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener { ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { mWaitingForResult = false; if (resultCode == RESULT_OK && mAddItemCellInfo != null) { switch (requestCode) { ... case REQUEST_PICK_APPWIDGET: addAppWidget(data); break; case REQUEST_CREATE_APPWIDGET: completeAddAppWidget(data, mAddItemCellInfo, !mDesktopLocked); break; } } ... } }
The method {@code addAppWidget()} checks to see if the app widget needs to be configured before it's added:
void addAppWidget(Intent data) { int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET); AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); if (appWidget.configure != null) { // Launch over to configure widget, if needed. Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); intent.setComponent(appWidget.configure); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); startActivityForResult(intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise, finish adding the widget. } }
For more discussion of configuration, see Creating an App Widget Configuration Activity.
Once the app widget is ready, the next step is to do the actual work of adding it to the workspace. The original Launcher uses a method called {@code completeAddAppWidget()} to do this.
Android 4.1 adds APIs for a more streamlined binding process. These APIs also make it possible for a host to provide a custom UI for binding. To use this improved process, your app must declare the {@link android.Manifest.permission#BIND_APPWIDGET} permission in its manifest:
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
But this is just the first step. At runtime the user must explicitly grant permission to your app to allow it to add app widgets to the host. To test whether your app has permission to add the widget, you use the {@link android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed bindAppWidgetIdIfAllowed()} method. If {@link android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed bindAppWidgetIdIfAllowed()} returns {@code false}, your app must display a dialog prompting the user to grant permission ("allow" or "always allow," to cover all future app widget additions). This snippet gives an example of how to display the dialog:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName); // This is the options bundle discussed above intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
The host also has to check whether the user added an app widget that needs configuration. For more discussion of this topic, see Creating an App Widget Configuration Activity.
The approach you use in implementing your host should depend on what Android version you're targeting. Many of the features described in this section were introduced in 3.0 or later. For example:
If you are targeting earlier devices, refer to the original Launcher as an example.
Widget developers can specify a number of configuration settings for widgets using the AppWidgetProviderInfo metadata. These configuration options, discussed in more detail below, can be retrieved by the host from the {@link android.appwidget.AppWidgetProviderInfo} object associated with a widget provider.
Regardless of the version of Android you are targeting, all hosts have the following responsibilities:
In addition to the requirements listed above, specific platform versions introduce features that place new responsibilities on the host. These are described in the following sections.
Android 3.0 (API Level 11) introduces the ability for a widget to specify {@link android.appwidget.AppWidgetProviderInfo#autoAdvanceViewId autoAdvanceViewId()}. This view ID should point to an instance of an {@link android.widget.Advanceable}, such as {@link android.widget.StackView} or {@link android.widget.AdapterViewFlipper}. This indicates that the host should call {@link android.widget.Advanceable#advance advance()} on this view at an interval deemed appropriate by the host (taking into account whether it makes sense to advance the widget—for example, the host probably wouldn't want to advance a widget if it were on another page, or if the screen were turned off).
Android 3.1 (API Level 12) introduces the ability to resize widgets. A widget can specify that it is resizable using the {@link android.appwidget.AppWidgetProviderInfo#resizeMode android:resizeMode} attribute in the {@link android.appwidget.AppWidgetProviderInfo} metadata, and indicate whether it supports horizontal and/or vertical resizing. Introduced in Android 4.0 (API Level 14), the widget can also specify a {@link android.appwidget.AppWidgetProviderInfo#minResizeWidth android:minResizeWidth} and/or {@link android.appwidget.AppWidgetProviderInfo#minResizeHeight android:minResizeHeight}.
It is the host’s responsibility to make it possible for the widget to be resized horizontally and/or vertically, as specified by the widget. A widget that specifies that it is resizable can be resized arbitrarily large, but should not be resized smaller than the values specified by {@link android.appwidget.AppWidgetProviderInfo#minResizeWidth android:minResizeWidth} and {@link android.appwidget.AppWidgetProviderInfo#minResizeHeight android:minResizeHeight}. For a sample implementation, see {@code AppWidgetResizeFrame} in {@code Launcher2}.
Android 4.0 (API Level 15) introduces a change in padding policy that puts the responsibility on the host to manage padding. As of 4.0, app widgets no longer include their own padding. Instead, the system adds padding for each widget, based the characteristics of the current screen. This leads to a more uniform, consistent presentation of widgets in a grid. To assist applications that host app widgets, the platform provides the method {@link android.appwidget.AppWidgetHostView#getDefaultPaddingForWidget getDefaultPaddingForWidget()}. Applications can call this method to get the system-defined padding and account for it when computing the number of cells to allocate to the widget.
Android 4.1 (API Level 16) adds an API that allows the widget provider to get more detailed information about the environment in which its widget instances are being hosted. Specifically, the host hints to the widget provider about the size at which the widget is being displayed. It is the host’s responsibility to provide this size information.
The host provides this information via {@link android.appwidget.AppWidgetHostView#updateAppWidgetSize updateAppWidgetSize()}. The size is specified as a minimum and maximum width/height in dps. The reason that a range is specified (as opposed to a fixed size) is because the width and height of a widget may change with orientation. You don’t want the host to have to update all of its widgets on rotation, as this could cause serious system slowdown. These values should be updated once upon the widget being placed, any time the widget is resized, and any time the launcher inflates the widget for the first time in a given boot (as the values aren’t persisted across boot).
Android 4.2 (API Level 17) adds the ability for the options bundle to be specified at bind time. This is the ideal way to specify app widget options, including size, as it gives the {@link android.appwidget.AppWidgetProvider} immediate access to the options data on the first update. This can be achieved by using the method {@link android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int,android.content.ComponentName,android.os.Bundle) bindAppWidgetIdIfAllowed()}. For more discussion of this topic, see Binding app widgets.
Android 4.2 also introduces lockscreen widgets. When hosting widgets on the lockscreen, the host must specify this information within the app widget options bundle (the {@link android.appwidget.AppWidgetProvider} can use this information to style the widget appropriately). To designate a widget as a lockscreen widget, use {@link android.appwidget.AppWidgetHostView#updateAppWidgetOptions updateAppWidgetOptions()} and include the field {@link android.appwidget.AppWidgetManager#OPTION_APPWIDGET_HOST_CATEGORY} with the value {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}. This option defaults to {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN}, so it is not explicitly required to set this for a home screen host.
Make sure that your host adds only app widgets that are appropriate for your app—for example, if your host is a home screen, ensure that the {@link android.appwidget.AppWidgetProviderInfo#widgetCategory android:widgetCategory} attribute in the {@link android.appwidget.AppWidgetProviderInfo} metadata includes the flag {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN}. Similarly, for the lockscreen, ensure that field includes the flag {@link android.appwidget.AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD}. For more discussion of this topic, see Enabling App Widgets on the Lockscreen.