Jeff Sharkey

Forecast widget for Android 1.5 (with source!)

Over the past few months I’ve been working on the new AppWidget framework that was released as part of the Android 1.5 SDK. I wanted to write a really in-depth widget and share it, so I decided to write a forecast widget.

It offers multiple configurations (both a 2×1 and tiny 1×1), and updates four times daily with the latest forecasts. You can also insert multiple widgets to keep track of the weather in different locations. And tapping the widget brings up a detailed forecast for the next few days. Here are some quick screenshots:

First, here’s the source code under an Apache 2.0 license. And you can grab an APK here.

Now, let’s talk about some of the details. We’re storing widget configuration and cached forecast data in a ContentProvider called ForecastProvider. Pretty simple stuff, and it offers two handy Uri’s to pick all forecasts for a widget using /appwidgets/[id]/forecasts, and only the most-current forecast using /appwidgets/[id]/forecast_at/[time]. These come in handy later when for the update process and details dialog. Also, WebserviceHelper performs most of the backend work of parsing XML returned from the API, and stuffing the forecasts and alerts into the ContentProvider.

As for updates, we maintain our own schedule that wakes us up each day 5:50AM, 11:50AM, 5:50PM, and 11:50PM. These times were mostly chosen to be equally spread out across the day, and to be ready before your 6AM alarm goes off. Notice I said your 6AM alarm, lol. The math also works out if you set your alarm for noon. 😉

Similar to the Wiktionary example, we handle our updates in UpdateService because we’re probably doing API queries that could expose our AppWidgetProvider to the ANR timeout. However, we handle things slightly different because we’re pushing unique updates for each widget. We push updates like this:

UpdateService.requestUpdate(new int[] { mAppWidgetId });
startService(new Intent(this, UpdateService.class));

Which is actually doing a synchronization dance behind the scenes to make sure we only ever have a single thread running updates. Multiple updates are just added to an internal queue. Once the update thread has cleared its work queue, the thread and Service terminate gracefully. This is very similar to how the Calendar widget handles it’s updates. During the update passes, we’re also a good citizen and only update forecasts if the data is more than 3 hours old. (We’re doing our part to keep the boot process spiffy and fast.)

The one final thing to mention is that when we’re building the PendingIntents for when you click on widgets, we’re using the ContentProvider Uri for the data field. This is because PendingIntents don’t keep unique extras bundles, and the details dialog needs a way of telling which widget was selected.

This is a pretty complex example, and it exercises most everything you’ll encounter when writing a widget. Not to mention it actually works and provides useful information! The data comes from the National Weather Service NDFD API, which offers awesome data under a public domain license. And the forecast icons came from the Tango Desktop Project, also under a public domain license.