An implementation pattern for updating Android widgets using alarms

I recently added a widget to the Shush! app.  I hadn't implemented a widget before, and I found that there was some complexity involved that I didn't anticipate, so I thought I'd share the approach I ended up taking.

Some quick background - Shush! is a utility app that lets you set a "quiet time" for your ringer, restoring it back after the duration you request.  I wanted a widget that would show the time remaining while in "Shush! mode" (among other features).

The widget therefore has two modes:

1) when Shush! mode is not active, the widget is visually static:

widget-inactive.png
 

2) when Shush! mode is active, the widget needs to be updated frequently (once per minute) to count down the remaining time

 

To implement the countdown behavior, when activating Shush! mode, I register a PendingIntent with the AlarmManager for one minute in the future.  The intent calls back to a BroadcastReceiver that updates the widget's display and registers another PendingIntent for a minute later.  When Shush! mode is deactivated, I unregister the outstanding alarm and switch the widget display to its static state.

OK, that wasn't too hard.  Ship it!

Hmm, wait a sec.  During testing, I noticed that during Shush! mode, even though I'm using a "non-waking" alarm, the alarms continued to be triggered while the screen is off.  It turns out that "screen off" != "sleeping".  It doesn't make sense to waste CPU cycles updating the widget if the screen is off, so to avoid that, when activating Shush! mode, I decided to register a BroadcastReceiver that filters for ACTION_SCREEN_OFF and ACTION_SCREEN_ON.  But (sigh) it turns out that one cannot set up a BroadcastReceiver that filters on these actions via the manifest (apparently it's a special case), so I needed to do it programmatically.  Of course, if you're going to do it programmatically, you have to keep that BroadcastReceiver instance around in memory for as long as you need it to be listening, so I created a Service to keep it alive.  I start the Service when Shush! mode is activated, and the Service creates and registers the screen on/off BroadcastReceiver.   When Shush! mode is deactivated, I stop the Service, which unregisters the BroadcastReceiver.

The BroadcastReceiver handles ACTION_SCREEN_OFF by canceling any outstanding alarm, and handles ACTION_SCREEN_ON by updating the widget, and setting a new alarm.

Great, that should do it.  Oh, wait, what about reboots?  Well, unfortunately Android forgets all alarms during a reboot, so they have to be reestablished again afterward.  I do this with a BroadcastReceiver that filters for BOOT_COMPLETED.  Upon receiving the broadcast, this receiver updates the widget and, if Shush! mode is activated, starts the Service.

So there you have it - a widget, a BroadcastReceiver to handle alarms, a BroadcastReceiver to handle screen on/off events, a BroadcastReceiver to handle reboots, and a Service to keep the screen on/off receiver alive.  Never a dull day as an Android developer!