August 27, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Creating a Home Screen App Widget on Android, Page 2

  • August 6, 2009
  • By Lauren Darcey, Shane Conder
  • Send Email »
  • More Articles »

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.

About the Authors

Shane Conder is a software developer focused on mobile and web technologies. He currently works at a small mobile software company. With almost two decades of experience in software production, Lauren Darcey specializes in the development of commercial grade mobile applications. Recently, Shane and Lauren coauthored an in-depth programming book entitled Android Wireless Application Development, available from Addison-Wesley (ISBN: 0321627091). They can be via their blog.



Originally published on http://www.developer.com.

Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel