393 lines
19 KiB
Plaintext
393 lines
19 KiB
Plaintext
|
page.title=App Widget Host
|
|||
|
page.tags="AppWidgetHost","home screen","launcher"
|
|||
|
@jd:body
|
|||
|
|
|||
|
<div id="qv-wrapper">
|
|||
|
<div id="qv">
|
|||
|
|
|||
|
<h2>In this document</h2>
|
|||
|
<ol>
|
|||
|
<li><a href="#host-binding">Binding App Widgets</a>
|
|||
|
<ol>
|
|||
|
<li><a href="#binding-pre">Binding app widgets on Android 4.0 and lower</a></li>
|
|||
|
<li><a href="#binding-41">Binding app widgets on Android 4.1 and higher</a></li>
|
|||
|
</ol>
|
|||
|
</li>
|
|||
|
<li><a href="#host-state">Host Responsibilities</a>
|
|||
|
<ol>
|
|||
|
<li><a href="#30">Android 3.0</a></li>
|
|||
|
<li><a href="#31">Android 3.1</a></li>
|
|||
|
<li><a href="#40">Android 4.0</a></li>
|
|||
|
<li><a href="#41">Android 4.1</li>
|
|||
|
<li><a href="#42">Android 4.2</a></li>
|
|||
|
</ol>
|
|||
|
</li>
|
|||
|
</ol>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
|
|||
|
<p>The Android Home screen available on most Android devices allows the user
|
|||
|
to embed <a href="{@docRoot}guide/topics/appwidgets/index.html">app widgets</a> 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.</p>
|
|||
|
|
|||
|
<p>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
|
|||
|
<a href="https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/Launcher.java">
|
|||
|
Launcher</a>.
|
|||
|
|
|||
|
|
|||
|
<p>Here is an overview of key classes and concepts involved in implementing a custom
|
|||
|
{@link android.appwidget.AppWidgetHost}:</p>
|
|||
|
<ul>
|
|||
|
<li><strong>App Widget Host</strong>—
|
|||
|
The {@link android.appwidget.AppWidgetHost} provides the interaction
|
|||
|
with the AppWidget service for apps, like the home screen, that want to embed
|
|||
|
app widgets in their UI. An {@link android.appwidget.AppWidgetHost} must have
|
|||
|
an ID that is unique within the host's own package. This ID remains persistent
|
|||
|
across all uses of the host. The ID is typically a hard-coded value that you assign
|
|||
|
in your application.</li>
|
|||
|
|
|||
|
<li><strong>App Widget ID</strong>—
|
|||
|
Each app widget instance is assigned a unique ID at the time of binding
|
|||
|
(see {@link android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed bindAppWidgetIdIfAllowed()},
|
|||
|
discussed in more detail in <a href="#binding">Binding app widgets</a>).
|
|||
|
The unique ID is obtained by the host using {@link android.appwidget.AppWidgetHost#allocateAppWidgetId() allocateAppWidgetId()}. This ID is persistent across the lifetime of the widget,
|
|||
|
that is, until it is deleted from the host. Any host-specific state (such as the
|
|||
|
size and location of the widget) should be persisted by the hosting package and
|
|||
|
associated with the app widget ID.
|
|||
|
</li>
|
|||
|
|
|||
|
<li><strong>App Widget Host View</strong>—
|
|||
|
{@link android.appwidget.AppWidgetHostView} can be thought of as a frame
|
|||
|
that the widget is wrapped in whenever it needs to be displayed. An app widget
|
|||
|
is assigned to an {@link android.appwidget.AppWidgetHostView} every time the
|
|||
|
widget is inflated by the host. </li>
|
|||
|
<li><strong>Options Bundle</strong>—
|
|||
|
The {@link android.appwidget.AppWidgetHost} uses the options bundle to communicate
|
|||
|
information to the {@link android.appwidget.AppWidgetProvider} about how the
|
|||
|
widget is being displayed (for example, size range, and whether the widget is on
|
|||
|
a lockscreen or the home screen). This information allows the
|
|||
|
{@link android.appwidget.AppWidgetProvider} to tailor the widget's contents
|
|||
|
and appearance based on how and where it is displayed.
|
|||
|
You use
|
|||
|
{@link android.appwidget.AppWidgetHostView#updateAppWidgetOptions(android.os.Bundle) updateAppWidgetOptions()}
|
|||
|
and
|
|||
|
{@link android.appwidget.AppWidgetHostView#updateAppWidgetSize updateAppWidgetSize()}
|
|||
|
|
|||
|
to modify an app widget's
|
|||
|
bundle. Both of these methods trigger a callback to the
|
|||
|
{@link android.appwidget.AppWidgetProvider}.</p></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="host-binding">Binding App Widgets</h2>
|
|||
|
|
|||
|
<p>When a user adds an app widget to a host, a process called
|
|||
|
<em>binding</em> occurs. <em>Binding</em> 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.</p>
|
|||
|
|
|||
|
<h3 id="binding-pre">Binding app widgets on Android 4.0 and lower</h3>
|
|||
|
|
|||
|
<p>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
|
|||
|
<a href="https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java">Launcher</a>. 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:</p>
|
|||
|
|
|||
|
<pre>
|
|||
|
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;
|
|||
|
}
|
|||
|
...
|
|||
|
}</pre>
|
|||
|
|
|||
|
<p>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:</p>
|
|||
|
|
|||
|
<pre>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;
|
|||
|
}
|
|||
|
}
|
|||
|
...
|
|||
|
}
|
|||
|
}</pre>
|
|||
|
|
|||
|
<p>The method {@code addAppWidget()} checks to see if the app widget
|
|||
|
needs to be configured before it's added:</p>
|
|||
|
|
|||
|
<pre>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.
|
|||
|
}
|
|||
|
}</pre>
|
|||
|
|
|||
|
<p>For more discussion of configuration,
|
|||
|
see <a href="{@docRoot}guide/topics/appwidgets/index.html#Configuring">Creating an
|
|||
|
App Widget Configuration Activity</a>.</p>
|
|||
|
|
|||
|
<p>Once the app widget is ready, the next step is to do the
|
|||
|
actual work of adding it to the workspace. The
|
|||
|
<a href="https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java">original Launcher</a> uses a method called {@code completeAddAppWidget()}
|
|||
|
to do this.</p>
|
|||
|
|
|||
|
<h3 id="binding-41">Binding app widgets on Android 4.1 and higher</h3>
|
|||
|
|
|||
|
<p>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:</p>
|
|||
|
|
|||
|
<pre><uses-permission android:name="android.permission.BIND_APPWIDGET" />
|
|||
|
</pre>
|
|||
|
|
|||
|
|
|||
|
<p>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:</p>
|
|||
|
|
|||
|
<pre>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);
|
|||
|
</pre>
|
|||
|
|
|||
|
<p>The host also has to check whether the user added
|
|||
|
an app widget that needs configuration. For more discussion of this topic,
|
|||
|
see
|
|||
|
<a href="{@docRoot}guide/topics/appwidgets/index.html#Configuring">Creating
|
|||
|
an App Widget Configuration Activity</a>.</p>
|
|||
|
|
|||
|
<h2 id="host-state">Host Responsibilities</h2>
|
|||
|
|
|||
|
<div class="sidebox-wrapper">
|
|||
|
<div class="sidebox">
|
|||
|
<h2>What Version are You Targeting?</h2>
|
|||
|
<p>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:</p>
|
|||
|
<ul>
|
|||
|
<li>Android 3.0 (API Level 11) introduces auto-advance behavior for widgets.</li>
|
|||
|
<li>Android 3.1 (API Level 12) introduces the ability to resize widgets.</li>
|
|||
|
<li>Android 4.0 (API Level 15) introduces a change in padding policy that
|
|||
|
puts the responsibility on the
|
|||
|
host to manage padding.</li>
|
|||
|
<li>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.</li>
|
|||
|
<li>Android 4.2 (API Level 17) introduces the options bundle and the
|
|||
|
{@link android.appwidget.AppWidgetManager#bindAppWidgetIdIfAllowed(int,android.content.ComponentName,android.os.Bundle) bindAppWidgetIdIfAllowed()}
|
|||
|
method. It also introduces lockscreen widgets.</li>
|
|||
|
</ul>
|
|||
|
<p>If you are targeting earlier devices, refer to the original
|
|||
|
<a href="https://android.googlesource.com/platform/packages/apps/Launcher/+/master/src/com/android/launcher/Launcher.java">Launcher</a> as an example.
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<p>Widget developers can specify a number of configuration settings
|
|||
|
for widgets using the <a href="{@docRoot}guide/topics/appwidgets/index.html#MetaData">
|
|||
|
AppWidgetProviderInfo metadata</a>.
|
|||
|
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.</p>
|
|||
|
|
|||
|
<p>Regardless of the version of Android you are targeting, all hosts
|
|||
|
have the following responsibilities:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>When adding a widget, you must allocate the widget ID as described above.
|
|||
|
You must also make sure that when a widget is removed from the host, you call {@link android.appwidget.AppWidgetHost#deleteAppWidgetId deleteAppWidgetId()}
|
|||
|
to deallocate the widget ID.</li>
|
|||
|
|
|||
|
<li>When adding a widget, be sure to launch its configuration activity
|
|||
|
if it exists, as described in
|
|||
|
<a href="{@docRoot}guide/topics/appwidgets/index.html#UpdatingFromTheConfiguration">
|
|||
|
Updating the App Widget
|
|||
|
from the Configuration Activity</a>. This is a necessary step for many app widgets before
|
|||
|
they can be properly displayed.</li>
|
|||
|
|
|||
|
<li>Every app widget specifies a minimum width and height in dps, as defined in the {@link android.appwidget.AppWidgetProviderInfo} metadata
|
|||
|
(using {@link android.appwidget.AppWidgetProviderInfo#minWidth android:minWidth} and
|
|||
|
{@link android.appwidget.AppWidgetProviderInfo#minHeight android:minHeight}).
|
|||
|
Make sure that the widget is laid out with at least this many dps.
|
|||
|
For example, many hosts align icons and widgets in a grid. In this scenario,
|
|||
|
by default the host should add the app widget using the minimum number of
|
|||
|
cells that satisfy the {@code minWidth} and {@code minHeight} constraints.</li>
|
|||
|
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>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.</p>
|
|||
|
|
|||
|
<h3 id="30">Android 3.0</h3>
|
|||
|
|
|||
|
<p>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).</p>
|
|||
|
|
|||
|
<h3 id="31">Android 3.1</h3>
|
|||
|
|
|||
|
<p>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}.</p>
|
|||
|
|
|||
|
<p>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 <a href="https://android.googlesource.com/platform/packages/apps/Launcher2/+/master/src/com/android/launcher2/AppWidgetResizeFrame.java">
|
|||
|
{@code AppWidgetResizeFrame}</a> in {@code Launcher2}.</p>
|
|||
|
|
|||
|
|
|||
|
<h3 id="40">Android 4.0</h3>
|
|||
|
|
|||
|
<p>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.</p>
|
|||
|
|
|||
|
<h3 id="41">Android 4.1</h3>
|
|||
|
|
|||
|
<p>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.</p>
|
|||
|
|
|||
|
<p>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).</p>
|
|||
|
|
|||
|
|
|||
|
<h3 id="42">Android 4.2</h3>
|
|||
|
|
|||
|
<p>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 <a href="#host-binding">Binding app widgets</a>.</p>
|
|||
|
|
|||
|
<p>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.</p>
|
|||
|
|
|||
|
<p>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
|
|||
|
<a href="{@docRoot}guide/topics/appwidgets/index.html#lockscreen">
|
|||
|
Enabling App Widgets on the Lockscreen</a>.</p>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|