diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java index b1dc3ad13f12f..9841815a52d19 100644 --- a/media/java/android/media/AudioFocusRequest.java +++ b/media/java/android/media/AudioFocusRequest.java @@ -40,7 +40,7 @@ import android.os.Looper; *

When an application requests audio focus, it expresses its intention to “own” audio focus to * play audio. Let’s review the different types of focus requests, the return value after a request, * and the responses to a loss. - *
Note: applications should not play anything until granted focus. + *

Note: applications should not play anything until granted focus.

* *

The different types of focus requests

*

There are four focus request types. A successful focus request with each will yield different @@ -77,9 +77,10 @@ import android.os.Looper; * *

An {@code AudioFocusRequest} instance always contains one of the four types of requests * explained above. It is passed when building an {@code AudioFocusRequest} instance with its - * builder in the {@link Builder} constructor {@link Builder#Builder(int)}, or with - * {@link Builder#setFocusGain(int)} after copying an existing instance with - * {@link Builder#Builder(AudioFocusRequest)}. + * builder in the {@link Builder} constructor + * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or + * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with + * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}. * *

Qualifying your focus request

*

Use case requiring a focus request

@@ -105,10 +106,11 @@ import android.os.Looper; *

Pausing vs ducking

*

When an application requested audio focus with * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus - * owner. Note that this behavior is new for Android O, whereas applications targeting SDK - * up to API 25, applications had to implement the ducking themselves when they received a focus + * owner. + *

Note: this behavior is new for Android O, whereas applications targeting + * SDK level up to API 25 had to implement the ducking themselves when they received a focus * loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. - *
But ducking is not always the behavior expected by the user. A typical example is when the + *

But ducking is not always the behavior expected by the user. A typical example is when the * device plays driving directions while the user is listening to an audio book or podcast, and * expects the audio playback to pause, instead of duck, as it is hard to understand a navigation * prompt and spoken content at the same time. Therefore the system will not automatically duck @@ -126,7 +128,92 @@ import android.os.Looper; * speech, you can also declare so with {@link Builder#setWillPauseWhenDucked(boolean)}, which will * cause the system to call your focus listener instead of automatically ducking. * + *

Example

+ *

The example below covers the following steps to be found in any application that would play + * audio, and use audio focus. Here we play an audio book, and our application is intended to pause + * rather than duck when it loses focus. These steps consist in: + *

    + *
  • Creating {@code AudioAttributes} to be used for the playback and the focus request.
  • + *
  • Configuring and creating the {@code AudioFocusRequest} instance that defines the intended + * focus behaviors.
  • + *
  • Requesting audio focus and checking the return code to see if playback can happen right + * away, or is delayed.
  • + *
  • Implementing a focus change listener to respond to focus gains and losses.
  • + *
+ *

+ *

+ * // initialization of the audio attributes and focus request
+ * mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
+ * mPlaybackAttributes = new AudioAttributes.Builder()
+ *         .setUsage(AudioAttributes.USAGE_MEDIA)
+ *         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ *         .build();
+ * mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ *         .setAudioAttributes(mPlaybackAttributes)
+ *         .setAcceptsDelayedFocusGain(true)
+ *         .setWillPauseWhenDucked(true)
+ *         .setOnAudioFocusChangeListener(this, mMyHandler)
+ *         .build();
+ * mMediaPlayer = new MediaPlayer();
+ * mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
+ * final Object mFocusLock = new Object();
+ *
+ * boolean mPlaybackDelayed = false;
+ *
+ * // requesting audio focus
+ * int res = mAudioManager.requestAudioFocus(mFocusRequest);
+ * synchronized (mFocusLock) {
+ *     if (res == AUDIOFOCUS_REQUEST_FAILED) {
+ *         mPlaybackDelayed = false;
+ *     } else if (res == AUDIOFOCUS_REQUEST_GRANTED) {
+ *         mPlaybackDelayed = false;
+ *         playbackNow();
+ *     } else if (res == AUDIOFOCUS_REQUEST_DELAYED) {
+ *        mPlaybackDelayed = true;
+ *     }
+ * }
+ *
+ * // implementation of the OnAudioFocusChangeListener
+ * @Override
+ * public void onAudioFocusChange(int focusChange) {
+ *     switch (focusChange) {
+ *         case AudioManager.AUDIOFOCUS_GAIN:
+ *             if (mPlaybackDelayed || mResumeOnFocusGain) {
+ *                 synchronized (mFocusLock) {
+ *                     mPlaybackDelayed = false;
+ *                     mResumeOnFocusGain = false;
+ *                 }
+ *                 playbackNow();
+ *             }
+ *             break;
+ *         case AudioManager.AUDIOFOCUS_LOSS:
+ *             synchronized (mFocusLock) {
+ *                 // this is not a transient loss, we shouldn't automatically resume for now
+ *                 mResumeOnFocusGain = false;
+ *                 mPlaybackDelayed = false;
+ *             }
+ *             pausePlayback();
+ *             break;
+ *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+ *             // we handle all transient losses the same way because we never duck audio books
+ *             synchronized (mFocusLock) {
+ *                 // we should only resume if playback was interrupted
+ *                 mResumeOnFocusGain = mMediaPlayer.isPlaying();
+ *                 mPlaybackDelayed = false;
+ *             }
+ *             pausePlayback();
+ *             break;
+ *     }
+ * }
+ *
+ * // Important:
+ * // Also set "mResumeOnFocusGain" to false when the user pauses or stops playback: this way your
+ * // application doesn't automatically restart when it gains focus, even though the user had
+ * // stopped it.
+ * 
*/ + public final class AudioFocusRequest { // default attributes for the request when not specified @@ -244,36 +331,15 @@ public final class AudioFocusRequest { /** * Builder class for {@link AudioFocusRequest} objects. - *

Here is an example where {@code Builder} is used to define the - * {@link AudioFocusRequest} for requesting audio focus: - * - *

-     * mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
-     * mPlaybackAttributes = new AudioAttributes.Builder()
-     *         .setUsage(AudioAttributes.USAGE_GAME)
-     *         .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
-     *         .build();
-     * mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
-     *         .setAudioAttributes(mPlaybackAttributes)
-     *         .setAcceptsDelayedFocusGain(true)
-     *         .setOnAudioFocusChangeListener(mMyFocusListener, mMyHandler)
-     *         .build();
-     * mMediaPlayer = new MediaPlayer();
-     *  ...
-     * mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
-     *  ...
-     * boolean mPlaybackAuthorized = true;;
-     * int res = mAudioManager.requestAudioFocus(mFocusRequest);
-     * if (res == AUDIOFOCUS_REQUEST_FAILED) {
-     *     mPlaybackAuthorized = false;
-     *     cancelPlayback();
-     * } else if (res == AUDIOFOCUS_REQUEST_DELAYED) {
-     *     playbackDelayed();
-     * } else { // res == AUDIOFOCUS_REQUEST_GRANTED
-     *     playbackNow();
-     * }
-     * 
- * + *

See {@link AudioFocusRequest} for an example of building an instance with this builder. + *
The default values for the instance to be built are: + * + * + * + * + * + *
focus listener and handlernone
{@code AudioAttributes}attributes with usage set to + * {@link AudioAttributes#USAGE_MEDIA}
pauses on duckfalse
supports delayed focus grantfalse
*/ public static final class Builder { private OnAudioFocusChangeListener mFocusListener;