How to _properly_ handle audio interruptions

No matter how great your app is, you can bet your users will hate you if you don’t pause audio playback when they take a phone call. Because the device isn’t going to let you play over something so important, so instead, that user will hang up to discover that they missed the last 12 minutes of their podcast and they have no idea what’s happening. And that’s on you.

So, pause audio playback when you lose audio focus. Audio focus refers to the assumption that only one thing should be playing at a time. Which means that the system needs a way to track which apps are currently playing audio, and which may be interested. Audio focus helps by playing the role of the conch shell — when you’re holding it, you get to speak. But the system will take it away when it’s someone else’s turn.

Consider a situation where your app is streaming music. The user wants an uninterrupted experience there, for the most part.

  • If the phone rings, and the user answers, they do not want that music to continue to play. Obviously. So the system will silence your app, and you should notice this transient loss of audio focus (it’ll be back after the call!) and pause the music while you wait.
  • If the user is following navigation instructions, those may speak over the music. You can react by lowering your volume temporarily (“ducking”) or, alternatively, pausing the music until the instructions are complete.
  • If the user is finished with music and decides to play a podcast (in a different app), this would cause a permanent loss of audio focus and your app should probably just stop.

Audio focus is the conch shell that alerts you to situations like this!

Fortunately, Ian Lake had a pretty thorough breakdown of audio focus in his Media Playback talk at BABBQ, so definitely check that out. But here’s a breakdown on reacting to those changes in audio focus.

You can manage audio focus for your app with AudioManager. When you’re ready to play something, you simply request it (and when you’re done, remember to release it). When audio focus is granted, you hold the conch shell and can have your playback. But, as I said, the system may take the audio focus back, either temporarily or permanently. So you need an OnAudioFocusChangeListener to keep track of your status and react to those changes! Which should probably look something like this:

AudioManager.OnAudioFocusChangeListener afChangeListener = 
new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback because your Audio Focus was
// temporarily stolen, but will be back soon.
// i.e. for a phone call
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// Stop playback, because you lost the Audio Focus.
// i.e. the user started some other playback app
// Remember to unregister your controls/buttons here.
// And release the kra — Audio Focus!
// You’re done.
am.abandonAudioFocus(afChangeListener);
} else if (focusChange ==
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume, because something else is also
// playing audio over you.
// i.e. for notifications or navigation directions
// Depending on your audio playback, you may prefer to
// pause playback here instead. You do you.
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback, because you hold the Audio Focus
// again!
// i.e. the phone call ended or the nav directions
// are finished
// If you implement ducking and lower the volume, be
// sure to return it to normal here, as well.
}
}
};

Feel free to copy and paste that to get started. Or to convert it to a switch statement and mock me on the internet. Either way, so long as you are properly using audio focus!

While older versions of Android required developers to rely on a PhoneStateListener as a signal for pausing playback during phone calls, this all changed in Lollipop. On Lollipop+ devices, the state of the phone is properly integrated into the audio focus state and your app will receive AUDIOFOCUS_LOSS_TRANSIENT when the call starts and AUDIOFOCUS_GAIN when it ends. One less API to get right!

For what it’s worth, as of Android 6.0, LISTEN_CALL_STATE doesn’t require the READ_PHONE_STATE permission anymore, which is good. But several of the other telephony options do, and you don’t want to risk poorly-written code asking your user for phone permissions when all it wants is to pause some music. That’s creepy.

Keep in mind though that even on pre-Lollipop devices, you should be using audio focus — only responding to the phone state means you may be missing other audio cues. Think of those navigation directions or another app being started. Your app should be working for your user, not annoying them until they uninstall you.

So give media playback a try — the right way. Depend on audio focus as a signal for how to create a seamless experience for your users. Because that is how you #BuildBetterApps.

Join the discussion on the Google+ post, and follow the Android Development Patterns Collection for more!

Like what you read? Give Joanna Smith a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.