App widgets are miniature app views that you can be embed in other
apps—such as the home screen—and receive periodic updates. These
views are referred to as
widgets
in the user interface, and you can publish
one with an app widget provider (or
widget provider
). An app component that
holds other widgets is called an app widget host (or
widget host
). Figure 1
shows a sample music widget:
This document describes how to publish a widget using a widget provider. For
details about creating your own
AppWidgetHost
to
host app widgets, see
Build a widget host
.
For information about how to design your widget, see
App widgets overview
.
Widget components
To create a widget, you need the following basic components:
AppWidgetProviderInfo
object
- Describes the metadata for a widget, such as the widget's layout, update
frequency, and
AppWidgetProvider
class.
AppWidgetProviderInfo
is
defined in XML
, as
described in this document.
AppWidgetProvider
class
- Defines the basic methods that let you programmatically interface with the
widget. Through it, you receive broadcasts when the widget is updated,
enabled, disabled, or deleted. You
declare
AppWidgetProvider
in the
manifest
and then
implement
it, as
described in this document.
- View layout
- Defines the initial layout for the widget. The layout is
defined in
XML
, as described in this document.
Figure 2 shows how these components fit into the overall app widget processing
flow.
If your widget needs user configuration, implement the app widget configuration
activity. This activity lets users modify widget settings—for example, the
time zone for a clock widget.
We also recommend the following improvements:
flexible widget layouts
,
miscellaneous enhancements
,
advanced widgets
,
collection widgets
, and
building a widget
host
.
The
AppWidgetProviderInfo
object defines the essential qualities of a widget.
Define the
AppWidgetProviderInfo
object in an XML resource file using a single
<appwidget-provider>
element and save it in the project's
res/xml/
folder.
This is shown in the following example:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/example_loading_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
The default home screen positions widgets in its window based on a grid of cells
that have a defined height and width. Most home screens only let widgets take on
sizes that are integer multiples of the grid cells—for example, two cells
horizontally by three cells vertically.
The widget sizing attributes let you specify a default size for your widget and
provide lower and upper bounds on the size of the widget. In this context, the
default size of a widget is the size that the widget takes on when it is first
added to the home screen.
The following table describes the
<appwidget-provider>
attributes pertaining
to widget sizing:
Attributes and description
|
targetCellWidth
and
targetCellHeight
(Android 12),
minWidth
and
minHeight
|
- Starting in Android 12, the
targetCellWidth
and
targetCellHeight
attributes specify the default size of the widget in terms of grid
cells. These attributes
are
ignored in Android 11
and lower, and
can
be ignored if the home screen doesn't
support a grid-based layout.
- The
minWidth
and
minHeight
attributes specify the default size of the widget
in dp. If the values for a widget's minimum width or height don't match
the dimensions of the cells, then the values are rounded up to the
nearest cell size.
We recommend specifying both sets of
attributes—
targetCellWidth
and
targetCellHeight
, and
minWidth
and
minHeight
—so that your app can fall back to using
minWidth
and
minHeight
if the user's device
doesn't support
targetCellWidth
and
targetCellHeight
. If supported, the
targetCellWidth
and
targetCellHeight
attributes
take precedence over the
minWidth
and
minHeight
attributes.
|
minResizeWidth
and
minResizeHeight
|
Specify the widget's absolute minimum size. These values specify the
size under which the widget is illegible or otherwise unusable. Using
these attributes lets the user resize the widget to a size that is smaller
than the default widget size. The
minResizeWidth
attribute is
ignored if it is greater than
minWidth
or if horizontal
resizing isn't enabled. See
resizeMode
. Likewise, the
minResizeHeight
attribute is ignored if it is greater than
minHeight
or if vertical resizing isn't enabled.
|
maxResizeWidth
and
maxResizeHeight
|
Specify the widget's recommended maximum size. If the values aren't
a multiple of the grid cell dimensions, they are rounded up to the nearest
cell size. The
maxResizeWidth
attribute is ignored if it is
smaller than
minWidth
or if horizontal resizing isn't
enabled. See
resizeMode
. Likewise,
the
maxResizeHeight
attribute is ignored if it is greater
than
minHeight
or if vertical resizing isn't enabled.
Introduced in Android 12.
|
resizeMode
|
Specifies the rules by which a widget can be resized. You can use this
attribute to make home screen widgets resizable horizontally, vertically,
or on both axes. Users touch & hold a widget to show its resize handles,
then drag the horizontal or vertical handles to change its size on the
layout grid. Values for the
resizeMode
attribute include
horizontal
,
vertical
, and
none
. To
declare a widget as resizable horizontally and vertically, use
horizontal|vertical
.
|
Example
To illustrate how the attributes in the preceding table affect widget sizing,
assume the following specifications:
- A grid cell is 30 dp wide and 50 dp tall.
- The following attribute specification is provided:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
Starting with Android 12:
Use the
targetCellWidth
and
targetCellHeight
attributes as the default
size of the widget.
The widget's size is 2x2 by default. The widget can be resized down to 2x1 or
up to 4x3.
Android 11 and lower:
Use the
minWidth
and
minHeight
attributes to compute the default size of
the widget.
The default width =
Math.ceil(80 / 30)
= 3
The default height =
Math.ceil(80 / 50)
= 2
The widget's size is 3x2 by default. The widget can be resized down to 2x1 or
up to full screen.
Additional widget attributes
The following table describes the
<appwidget-provider>
attributes pertaining
to qualities other than widget sizing.
Attributes and description
|
updatePeriodMillis
|
Defines how often the widget framework requests an update from the
AppWidgetProvider
by calling the
onUpdate()
callback method. The actual update isn't guaranteed to occur exactly on
time with this value, and we recommend updating as infrequently as
possible—no more than once an hour—to conserve the battery.
For the full list of considerations to pick an appropriate update period,
see
Optimizations for updating widget
content
.
|
initialLayout
|
Points to the layout resource that defines the widget layout.
|
configure
|
Defines the activity that launches when the user adds the widget,
letting them configure widget properties. See
Enable users to configure widgets
.
Starting in Android 12, your app can skip the initial
configuration. See
Use the
widget's default configuration
for details.
|
description
|
Specifies the description for the widget picker to display for your
widget. Introduced in Android 12.
|
previewLayout
(Android 12)
and
previewImage
(Android 11 and lower)
|
- Starting in Android 12, the
previewLayout
attribute specifies a scalable preview, which
you provide as an XML layout set to the widget's default size. Ideally,
the layout XML specified as this attribute is the same layout XML as the
actual widget with realistic default values.
- In Android 11 or lower, the
previewImage
attribute specifies a preview of what the widget looks like after it's
configured, which the user sees when selecting the app widget. If not
supplied, the user instead sees your app's launcher icon. This
field corresponds to the
android:previewImage
attribute in
the
<receiver>
element in the
AndroidManifest.xml
file.
Note:
We recommend specifying both the
previewImage
and
previewLayout
attributes so that your app can fall back
to using
previewImage
if the user's device doesn't support
previewLayout
. For more details, see
Backward compatibility with scalable
widget previews
.
|
autoAdvanceViewId
|
Specifies the view ID of the widget subview that is auto-advanced by
the widget's host.
|
widgetCategory
|
Declares whether your widget can be displayed on the home screen
(
home_screen
), the lock screen (
keyguard
), or
both. For Android 5.0 and higher, only
home_screen
is valid.
|
widgetFeatures
|
Declares features supported by the widget. For example, if you want
your widget to use its default configuration when a user adds it, specify
both the
configuration_optional
and
reconfigurable
flags. This bypasses launching the configuration activity after a user
adds the widget. The user can still
reconfigure the widget
afterward.
|
The
AppWidgetProvider
class handles widget broadcasts and updates the widget
in response to widget lifecycle events. The following sections describe how to
declare
AppWidgetProvider
in the manifest and then implement it.
Declare a widget in the manifest
First, declare the
AppWidgetProvider
class in your app's
AndroidManifest.xml
file, as shown in the following example:
<receiver android:name="ExampleAppWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
The
<receiver>
element requires the
android:name
attribute, which specifies
the
AppWidgetProvider
used by the widget. The component must not be exported
unless a separate process needs to broadcast to your
AppWidgetProvider
, which
usually isn't the case.
The
<intent-filter>
element must include an
<action>
element with the
android:name
attribute. This attribute specifies that the
AppWidgetProvider
accepts the
ACTION_APPWIDGET_UPDATE
broadcast. This is the only broadcast that you must explicitly declare. The
AppWidgetManager
automatically sends all other widget broadcasts to the
AppWidgetProvider
as
necessary.
The
<meta-data>
element specifies the
AppWidgetProviderInfo
resource and
requires the following attributes:
android:name
: specifies the metadata name. Use
android.appwidget.provider
to identify the data as the
AppWidgetProviderInfo
descriptor.
android:resource
: specifies the
AppWidgetProviderInfo
resource
location.
The
AppWidgetProvider
class extends
BroadcastReceiver
as a
convenience class to handle widget broadcasts. It receives only the event
broadcasts that are relevant to the widget, such as when the widget is updated,
deleted, enabled, and disabled. When these broadcast events occur, the following
AppWidgetProvider
methods are called:
onUpdate()
- This is called to update the widget at intervals defined by the
updatePeriodMillis
attribute in the
AppWidgetProviderInfo
. See the
table
describing additional widget attributes
in this page for
more information.
- This method is also called when the user adds the widget, so it performs the
essential setup such as defining event handlers for
View
objects or starting jobs to load data to
display in the widget. However, if you declare a configuration activity without
the
configuration_optional
flag, this method is
not
called when the user
adds the widget, but it
is
called for the subsequent updates. It is the
responsibility of the configuration activity to perform the first update when
configuration is complete. See
Enable users to configure app widgets
for more information.
- The most important callback is
onUpdate()
. See
Handle events with the
onUpdate()
class
in this page for more information.
onAppWidgetOptionsChanged()
This is called when the widget is first placed and any time the widget is
resized. Use this callback to show or hide content based on the widget's size
ranges. Get the size ranges—and, starting in Android 12,
the list of possible sizes a widget instance can take—by calling
getAppWidgetOptions()
,
which returns a
Bundle
that includes the
following:
OPTION_APPWIDGET_MIN_WIDTH
:
contains the lower bound on the width, in dp units, of a widget instance.
OPTION_APPWIDGET_MIN_HEIGHT
:
contains the lower bound on the height, in dp units, of a widget instance.
OPTION_APPWIDGET_MAX_WIDTH
:
contains the upper bound on the width, in dp units, of a widget instance.
OPTION_APPWIDGET_MAX_HEIGHT
:
contains the upper bound on the height, in dp units, of a widget instance.
OPTION_APPWIDGET_SIZES
:
contains the list of possible sizes (
List<SizeF>
), in dp units, that a
widget instance can take. Introduced in Android 12.
onDeleted(Context, int[])
This is called every time a widget is deleted from the widget host.
onEnabled(Context)
This is called when an instance of the widget is created for the first time.
For example, if the user adds two instances of your widget, this is only called
the first time. If you need to open a new database or perform another setup that
only needs to occur once for all widget instances, then this is a good place to
do it.
onDisabled(Context)
This is called when the last instance of your widget is deleted from the
widget host. This is where you clean up any work done in
onEnabled(Context)
,
such as deleting a temporary database.
onReceive(Context, Intent)
This is called for every broadcast and before each of the preceding callback
methods. You normally don't need to implement this method, because the default
AppWidgetProvider
implementation filters all widget broadcasts and calls the
preceding methods as appropriate.
You must declare your
AppWidgetProvider
class implementation as a broadcast
receiver using the
<receiver>
element in the
AndroidManifest
. See
Declare a
widget in the manifest
in this page for more information.
Handle events with the onUpdate() class
The most important
AppWidgetProvider
callback is
onUpdate()
, because it is
called when each widget is added to a host, unless you use a configuration
activity without the
configuration_optional
flag. If your widget accepts any
user interaction events, then register the event handlers in this callback. If
your widget doesn't create temporary files or databases, or perform other work
that requires clean-up, then
onUpdate()
might be the only callback method you
need to define.
For example, if you want a widget with a button that launches an activity when
tapped, you can use the following implementation of
AppWidgetProvider
:
Kotlin
class ExampleAppWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
// Perform this loop procedure for each widget that belongs to this
// provider.
appWidgetIds.forEach { appWidgetId ->
// Create an Intent to launch ExampleActivity.
val pendingIntent: PendingIntent = PendingIntent.getActivity(
/* context = */ context,
/* requestCode = */ 0,
/* intent = */ Intent(context, ExampleActivity::class.java),
/* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// Get the layout for the widget and attach an onClick listener to
// the button.
val views: RemoteViews = RemoteViews(
context.packageName,
R.layout.appwidget_provider_layout
).apply {
setOnClickPendingIntent(R.id.button, pendingIntent)
}
// Tell the AppWidgetManager to perform an update on the current
// widget.
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
Java
public class ExampleAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Perform this loop procedure for each widget that belongs to this
// provider.
for (int i=0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
/* context = */ context,
/* requestCode = */ 0,
/* intent = */ intent,
/* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Get the layout for the widget and attach an onClick listener to
// the button.
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current app
// widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
This
AppWidgetProvider
defines only the
onUpdate()
method, using it to
create a
PendingIntent
that launches
an
Activity
and attaches it to the widget's
button using
setOnClickPendingIntent(int,
PendingIntent)
. It includes a loop that iterates through each entry
in
appWidgetIds
, which is an array of IDs that identify each widget created by
this provider. If the user creates more than one instance of the widget, then
they all update simultaneously. However, only one
updatePeriodMillis
schedule
is managed for all instances of the widget. For example, if the update schedule
is defined to be every two hours, and a second instance of the widget is added
one hour after the first one, then they're both updated on the period defined by
the first, and the second update period is ignored. They both update every two
hours, not every hour.
See the
ExampleAppWidgetProvider.java
sample class for more details.
Receive widget broadcast intents
AppWidgetProvider
is a convenience class. If you want to receive the widget
broadcasts directly, you can implement your own
BroadcastReceiver
or override
the
onReceive(Context,Intent)
callback. The intents you need to care about are the
following:
Create the widget layout
You must define an initial layout for your widget in XML and save it in the
project's
res/layout/
directory. Refer to
Design
guidelines
for details.
Creating the widget layout is straightforward if you're familiar with
layouts
. However, be aware that widget
layouts are based on
RemoteViews
,
which doesn't support every kind of layout or view widget. You can't use custom
views or subclasses of the views that are supported by
RemoteViews
.
RemoteViews
also supports
ViewStub
,
which is an invisible, zero-sized
View
you can use to lazily inflate layout
resources at runtime.
Support for stateful behavior
Android 12 adds support for stateful behavior using the following
existing components:
The widget is still stateless. Your app must store the state and register for
state change events.
The following code example shows how to implement these components.
Kotlin
// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)
// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)
// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
R.id.my_checkbox,
RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)
Java
// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);
// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);
// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
R.id.my_checkbox,
RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));
Provide two layouts: one targeting devices running Android 12 or
higher in
res/layout-v31
, and the other targeting previous
Android 11 or lower in the default
res/layout
folder.
Implement rounded corners
Android 12 introduces the following system parameters to set the
radii of your widget's rounded corners:
The following example shows a widget that uses
system_app_widget_background_radius
for the corner of the widget and
system_app_widget_inner_radius
for views inside the widget.
1
Corner of the widget.
2
Corner of a view inside the widget.
Important considerations for rounded corners
- Third-party launchers and device manufacturers can override the
system_app_widget_background_radius
parameter to be smaller than 28 dp.
The
system_app_widget_inner_radius
parameter is always 8 dp less than
the value of
system_app_widget_background_radius
.
- If your widget doesn't use
@android:id/background
or define a background
that clips its content based on the outline—with
android:clipToOutline
set to
true
—the launcher automatically identifies the background and
clips the widget using a rectangle with rounded corners of up to 16 dp.
See
Ensure your widget is compatible with
Android 12
.
For widget compatibility with previous versions of Android, we recommend
defining custom attributes and using a custom theme to override them for
Android 12, as shown in the following sample XML files:
/values/attrs.xml
<resources>
<attr name="backgroundRadius" format="dimension" />
</resources>
/values/styles.xml
<resources>
<style name="MyWidgetTheme">
<item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
</style>
</resources>
/values-31/styles.xml
<resources>
<style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
</style>
</resources>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?attr/backgroundRadius" />
...
</shape>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:background="@drawable/my_widget_background" />