Creating a Home Screen App Widget on Android

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.

Transcript of Creating a Home Screen App Widget on Android

Page 1: Creating a Home Screen App Widget on Android

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.

Page 2: Creating a Home Screen App Widget on Android

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) {

Page 3: Creating a Home Screen App Widget on Android

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(

Page 4: Creating a Home Screen App Widget on Android

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 });

Page 5: Creating a Home Screen App Widget on Android

// 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.