Sunteți pe pagina 1din 5

Creating a Home Screen App Widget on Android

Android users got their first taste of the App Widget when some basic controls like the Clock
and Picture Frame Home Screen controls shipped with the first Android handsets. However,
the App Widget API was not publicly available for developers to use until recently. This
interface gives developers two new and interesting ways to provide Android application
functionality outside the traditional boundaries of a phone application. Developers can use
the App Widget API (released in Android 1.5) to create simple controls and craft new App
Widget hosts to display and use these controls.

This article shows you how to create a Home Screen App Widget that updates at a user-
configured time interval by using the AlarmManager interface. Specifically, you will see how
to create an App Widget that displays an image chosen randomly from a set of images. The
displayed image changes periodically according to a timing interval configured by the user.

Creating a simple App Widget involves several steps:

1. Create a RemoteView, which provides the user interface for the App Widget.
2. Tie the RemoteView to an Activity that implements the AppWidgetProvider interface.
3. Provide key App Widget configuration information in the Android manifest file.

Preparing Your Project for an App Widget

An App Widget is basically just a BroadcastReceiver that handles specific actions. The
AppWidgetProvider interface simplifies handling these actions by providing a framework for
developers to implement the following methods:

onEnabled(): Called when the first App Widget is created. Global initialization should take
place here, if applicable.
onDisabled(): The inverse of the onEnabled() method, called when the last App Widget
handled by this definition is deleted. Global cleanup should take place here, if applicable.
onUpdate(): Called when the App Widget needs to update its View, which could be when
the user first creates the widget.
onDeleted(): Called when a particular instance of this App Widget is deleted. Cleanup for
the specific instance in question should take place here.
onReceive(): The default implementation of this method handles the BroadcastReceiver
actions and makes the appropriate calls to the methods shown above. (Warning: A well-
documented defect exists that requires the developer to handle certain cases explicitly. See
the following note for more information.)

Author's Note: Check this link for more


information about the current defects of the
AppWidget framework. The discussion
includes code to work around the issue (also
found in the downloadable sample code for
this article) and instances in which App Widget
identifiers can be passed to the onUpdate()
method even when they don't actually exist.
For the Android system to know about the App Widget, place a standard <receiver> tag in
the Android manifest file. The following snippet shows an example:

<receiver android:name="ImagesWidgetProvider">
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/imageswidget_info" />
</receiver>

You'll notice that, unlike a typical <receiver> definition, a <meta-data> section references
an XML file resource. This file defines addition data for App Widget, corresponding to the
AppWidgetProviderInfo class. The information defined here is immutable, so, this example
does not include the value for updatePeriodMillis, because this app lets users modify the
timing between updates—which can't be done if you assign updatePeriodMillis here. The
following code shows the complete imageswidget_info.xml file:

<?xml version="1.0" encoding="utf-8"?>


<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dp"
android:minHeight="146dp"
android:initialLayout="@layout/widget"
android:configure=
"com.mamlambo.imageswidget.ImagesWidgetConfiguration" />

The <appwidget-provider> tag defines the size of the App Widget, the default layout to
use, and the configuration Activity to launch whenever an instance of the App Widget is
created. To display nicely on the Home screen, widgets must adhere to certain size
guidelines. The Home screen is divided into cells of a particular size. The basic formula
(helpfully provided by Google) is to multiply the number of cells you want to occupy by 74,
and then subtract 2. In this case, the App Widget should be square: two cells high and two
cells wide. Therefore, use a size of ((74*2)-2), or 146.

Implementing onUpdate()

An App Widget that doesn't display anything isn't very useful. Luckily, the implementation
for the RemoteView object of this App Widget is straightforward; it uses a set of images
stored in the drawable resource directory of the application. The application references these
image resources as R.drawable.[imagename]. The code creates an array holding the names,
which makes it easy to randomly draw one of the images. The following snippet shows the
onUpdate() implementation, which randomly draws one of the included images:

@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
int imageNum = (new
java.util.Random().nextInt(IMAGES.length));
RemoteViews remoteView = new
RemoteViews(context.getPackageName(),
R.layout.widget);
remoteView.setImageViewResource(
R.id.image, IMAGES[imageNum]);
appWidgetManager.updateAppWidget(
appWidgetId, remoteView);
}
}

Note that the onUpdate() method expects a list of App Widget instances as the last
parameter. Each instance must be handled separately. Due to existing defects with the App
Widget framework, some instances provided may not be visible or enabled; however you can
safely ignore the problem for this example. Bear in mind that your particular implementation
may want to track which App Widgets are actually active.

You can review the simple R.layout.widget XML layout definition within the included
code download; it is basically just an ImageView. RemoteViews can use only a limited set of
View objects, including Button, ImageButton, ImageView, TextView, AnalogClock,
Chronometer, and ProgressBar—and only within FrameLayout, LinearLayout, or
RelativeLayout. Keep the RemoteView simple, because access is controlled through such
methods as setImageViewResource() and setTextViewText(). Their purpose is to draw a
View within another process, thus your application has less control over them than a normal
layout.

At this point, the basics of the App Widget are complete. However, to allow users to
configure the time between image updates, you must implement the configuration Activity
and then handle scheduling of the RemoteView updates.

Implementing the App Widget Configuration Activity

The configuration Activity, defined in the manifest file as ImagesWidgetConfiguration, is


like any Activity, with two exceptions:

1. When launched, it's intended to return a result, so the implementation must always call the
setResult() method with an appropriate result (either RESULT_CANCELED or
RESULT_OK).
2. When setting the result, the App Widget identifier value must be placed in the extra
referenced by AppWidgeManager.EXTRA_APPWIDGET_ID.

The following snippet of code demonstrates dealing with both exceptions, calling
setResult() and setting the default result as RESULT_CANCELED in case the user backs out of
the Activity:

Bundle extras = launchIntent.getExtras();


if (extras != null) {
appWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
Intent cancelResultValue = new Intent();
cancelResultValue.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_CANCELED, cancelResultValue);
}

Aside from those two restrictions, you may implement the configuration Activity however
you like. Figure 1 shows the simple configuration Activity for this example. If you return
RESULT_CANCELED, the App Widget won't be displayed to the user. If you return RESULT_OK,
it will be. You can use any storage mechanism you prefer for saving configuration data for
this particular instance of the App Widget. The example uses the SharedPreferences interface,
storing the time between updates with the particular App Widget identifier. You can see the
full implementation in the downloadable code for this article.

Figure 1. Configuration Screen: This simple configuration screen lets users set the time interval
between image display changes for this App Widget.

Implementing the Update Scheduling

As mentioned earlier, the configuration values within the AppWidgetProviderInfo class are
immutable. Because the updateTimeMillis value is within this class, any App Widget
instance created with an AppWidgetProviderInfo that has this value set will update at that
frequency, with no way to change it. Because this application should let users configure the
frequency at which the images are displayed within each App Widget instance, you must
implement the functionality yourself.

The AlarmManager class is the most logical update mechanism to use, because it lets
supports recurring notifications. These notifications are simple PendingIntent objects that will
be triggered.

You might think that you can just create an Intent object with the action value of
AppWidgetManager.ACTION_APPWIDGET_UPDATE and set the extras value to the particular
App Widget identifier. Then you would simply schedule it to update repeatedly by calling the
AlarmManager's setRepeating() method. Unfortunately, this does not work. The Android
system reuses Intents that match both action and scheme values—the "extras" values are not
compared. In practice, this means the Intent for each App Widget identifier would actually be
the same Intent. Fortunately, the solution is straightforward: define a scheme for your App
Widget and use it to define unique Intent instances. The following code snippet demonstrates
how to do this:

Intent widgetUpdate = new Intent();


widgetUpdate.setAction(
AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
new int[] { appWidgetId });
// make this pending intent unique
widgetUpdate.setData(
Uri.withAppendedPath(Uri.parse(
ImagesWidgetProvider.URI_SCHEME + "://widget/id/"),
String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(
getApplicationContext(), 0, widgetUpdate,
PendingIntent.FLAG_UPDATE_CURRENT);
// now schedule it
AlarmManager alarms = (AlarmManager) getApplicationContext().
getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(), updateRateSeconds * 1000,
newPending);

You'll find the preceding code in the ImagesWidgetConfiguration Activity. The manifest file
shows the <receiver> block for handling this particular scheme. You will also need to halt
the repeating alarm within onDeleted() method in the AppWidgetProvider interface.
Finally, when the handset restarts, update scheduling must be restarted as well.

Figure 2. Multiple Widget Instances: This figure shows two different instances of the final App
Widget running at the same time.

A straightforward solution to this involves leveraging some logic directly within the
AppWidgetProvider's onReceive() method. First, check for the update action. If it's an
update, ensure that it doesn't contain a scheme value. If it does not, then the PendingIntent,
scheduled with the AlarmManager, did not trigger this action. In that case, check to see if
there is a configured preference value for each of the App Widget identifiers. If not, then you
know this update action was received before the App Widget has been configured. However,
if the preference value is available, then you know you can schedule the PendingIntent.
Again, you can see this full logic implemented within the onReceive() method.

This scheme allows multiple App Widgets of a given type to be displayed simultaneously, as
shown in Figure 2. The updates can occur at different frequencies, and each displayed image
is selected randomly from the included set.

You've seen how to create a basic App Widget on the Android platform. Each instance of this
App Widget (shown on the Home screen) runs on a separate schedule, configured by the user,
even though that capability is not available directly through the App Widget framework. A
future article will show you how to expand the functionality of this App Widget to use
images downloaded from the Internet, and explain how to enhance it with an on-screen
interface to control the display of images directly.

S-ar putea să vă placă și