As for a simple alarm, I ended up just going to the source: the AlarmClock implementation in the Android source itself. And the purpose of this note is to summarize some of the things you've got to deal with when implementing an alarm on Android, some of which I had not known about until a stroll through the Android code itself, the 2.3 version of which is available on github here: https://github.com/android/platform_packages_apps_alarmclock. You can get the latest Android source at http://source.android.com/source/downloading.html, but it requires a few extra steps (I've been waiting more than an hour for the source to be completely downloaded with their "repo" tool - and will update my notes here if anything noteworthy has changed in the their AlarmClock implementation).
This particular note is not a code-snippet post, but a more general summarization for what on the surface seems a simple thing to do, and the mildly surprising degree one has to go so that the user has a smooth and uneventful experience.
Basic Idea of the Implementation
The basic idea in implementing any kind of solid alarm is to use the android.app.AlarmManager to set the alarm, which will call your custom android.content.BroadcastReceiver at the desired time. The system will "wake up" your Broadcast receiver for this, which is nice. Your app itself does not need to be running. I will refer to this particular BroadcastReceiver as the "Alarm Receiver".
You probably shouldn't put the Alarm Receiver in an activity of your app
It is possible to define a BroadcastReceiver in one of your activities and register it there. You can even use the AlarmManager to have this BroadcastReceiver called at some point in the future. This works as long as your activity is running. As soon as your activity goes away, however, any receivers defined in it have to go away, too. In fact, if you don't unregister a receiver, you'll get an error about IntentReceiverLeaked. And if you have to unregister the receiver for the alarm when the activity's onDestroy is called, this means that your app itself won't be woken up when the alarm goes off. Wonderful.
The Alarm Receiver only has a short window to do its work
Your Alarm Receiver has only a short time do its work before the system will kill it. So this means you need to set up other things to happen right away, or deal with wakelocks. I don't know what the exact time limit is. In the relevant Android AlarmClock source, they first get a wakelock (via static methods of their AlarmAlertWakeLock class) before doing some transactions against a content provider, and a number of other things.
What is the state of the KeyGuard?
The Android AlarmClock shows different activities based on whether the keyboard is in restricted input mode or not (it uses a full screen activity if it is). This can be the case when screen is locked. The Android folks uses a special full screen activity in this case.
When the user is to be notified that alarm has gone off, start your "Alert Activity" properly
In the Android source, when they launch the UI activity, they make sure and use Intent.FLAG_ACTIVITY_NO_USER_ACTION. Per the Android docs:
Typically, an activity can rely on that callback to indicate that an explicit user action has caused their activity to be moved out of the foreground. The callback marks an appropriate point in the activity's lifecycle for it to dismiss any notifications that it intends to display "until the user has seen them," such as a blinking LED.
If an activity is ever started via any non-user-driven events such as phone-call receipt or an alarm handler, this flag should be passed to
Context.startActivity, ensuring that the pausing activity does not think the user has acknowledged its notification.
So, this is one more scenario that must be dealt with.
Who plays the alarm?
All you want to do is play the alarm, right? Shouldn't the activity do it? Well, that's not what the Android folks ultimately decided to do. In fact, they play the alarm (and optionally vibrate) in a service so that it can continue to play if another activity overrides the AlarmAlert dialog/screen.
What if the user is on the phone when the alarm goes off?
The user might be on the phone when the alarm goes off, so you've got to decide what you're gonna do. You want the alarm to still be active, but not disrupt the ongoing call. The Android folks play the a special "in call alarm" (R.raw.in_call_alarm), and at a lower volume: a "volume suggested by [the] media team for in-call alarms".
What if the ringtone/alarm file is on the SD card, which is busy?
I guess this can happen. Basically, if playing the alarm with the MediaPlayer fails, they try to play the "fallback ringtone", which is wrapped up in the Android source at com.android.internal.R.raw.fallbackring... and don't forget to reset the MediaPlayer first before doing this.
How long do you play the alarm if the user doesn't stop it?
You've got to deal with this situation. The Android folks use a hard-coded ten minutes.
What if the device is rebooted before the alarm goes off?
The Android AlarmClock stores all of the alarms in an sqlite database, and these are checked after the device reboots by the AlarmInitReceiver, which is registered to be notified when the boot process is completed. You will need to decide what you're gonna do.
What if the screen is locked between time that the alarm is fired but before the default activity is shown has finished handling user input?
The Android AlarmClock specifically treats the case where
- the alarm has gone off
- the screen is not locked
- the AlarmAlert activity has been created (this is a dialog), and then
- the screen is locked
In this case, they explicitly call the full-screen version (which is a superclass of AlarmAlert) and kill the one that started first.
There may be some cases missed above - my own little walk in the forest will reveal as much - but the point is that it is perhaps more involved than one might think to have something as boring as an alarm clock work properly.