/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;

A class to encapsulate information about an audio focus request. An AudioFocusRequest instance is built by Builder, and is used to request and abandon audio focus, respectively with AudioManager.requestAudioFocus(AudioFocusRequest) and AudioManager.abandonAudioFocusRequest(AudioFocusRequest).

What is audio focus?

Audio focus is a concept introduced in API 8. It is used to convey the fact that a user can only focus on a single audio stream at a time, e.g. listening to music or a podcast, but not both at the same time. In some cases, multiple audio streams can be playing at the same time, but there is only one the user would really listen to (focus on), while the other plays in the background. An example of this is driving directions being spoken while music plays at a reduced volume (a.k.a. ducking).

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.

The different types of focus requests

There are four focus request types. A successful focus request with each will yield different behaviors by the system and the other application that previously held audio focus.

  • AudioManager.AUDIOFOCUS_GAIN expresses the fact that your application is now the sole source of audio that the user is listening to. The duration of the audio playback is unknown, and is possibly very long: after the user finishes interacting with your application, (s)he doesn’t expect another audio stream to resume. Examples of uses of this focus gain are for music playback, for a game or a video player.
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT is for a situation when you know your application is temporarily grabbing focus from the current owner, but the user expects playback to go back to where it was once your application no longer requires audio focus. An example is for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events ends, and if the user was listening to music when it started, the user expects music to resume, but didn’t wish to listen to both at the same time.
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: this focus request type is similar to AUDIOFOCUS_GAIN_TRANSIENT for the temporary aspect of the focus request, but it also expresses the fact during the time you own focus, you allow another application to keep playing at a reduced volume, “ducked”. Examples are when playing driving directions or notifications, it’s ok for music to keep playing, but not loud enough that it would prevent the directions to be hard to understand. A typical attenuation by the “ducked” application is a factor of 0.2f (or -14dB), that can for instance be applied with MediaPlayer.setVolume(0.2f) when using this class for playback.
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE is also for a temporary request, but also expresses that your application expects the device to not play anything else. This is typically used if you are doing audio recording or speech recognition, and don’t want for examples notifications to be played by the system during that time.

An AudioFocusRequest instance always contains one of the four types of requests explained above. It is passed when building an AudioFocusRequest instance with its builder in the Builder constructor Builder(int), or with Builder.setFocusGain(int) after copying an existing instance with Builder(AudioFocusRequest).

Qualifying your focus request

Use case requiring a focus request

Any focus request is qualified by the AudioAttributes (see Builder.setAudioAttributes(AudioAttributes)) that describe the audio use case that will follow the request (once it's successful or granted). It is recommended to use the same AudioAttributes for the request as the attributes you are using for audio/media playback.
If no attributes are set, default attributes of AudioAttributes.USAGE_MEDIA are used.

Delayed focus

Audio focus can be "locked" by the system for a number of reasons: during a phone call, when the car to which the device is connected plays an emergency message... To support these situations, the application can request to be notified when its request is fulfilled, by flagging its request as accepting delayed focus, with Builder.setAcceptsDelayedFocusGain(boolean).
If focus is requested while being locked by the system, AudioManager.requestAudioFocus(AudioFocusRequest) will return AudioManager.AUDIOFOCUS_REQUEST_DELAYED. When focus isn't locked anymore, the focus listener set with Builder.setOnAudioFocusChangeListener(OnAudioFocusChangeListener) or with Builder.setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler) will be called to notify the application it now owns audio focus.

Pausing vs ducking

When an application requested audio focus with AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, the system will duck the current 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 AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK.

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 when it detects it would be ducking spoken content: such content is detected when the AudioAttributes of the player are qualified by AudioAttributes.CONTENT_TYPE_SPEECH. Refer for instance to Builder.setContentType(int) and MediaPlayer.setAudioAttributes(AudioAttributes) if you are writing a media playback application for audio book, podcasts... Since the system will not automatically duck applications that play speech, it calls their focus listener instead to notify them of AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK, so they can pause instead. Note that this behavior is independent of the use of AudioFocusRequest, but tied to the use of AudioAttributes.

If your application requires pausing instead of ducking for any other reason than playing speech, you can also declare so with 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 AudioAttributes to be used for the playback and the focus request.
  • Configuring and creating the 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 == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
        mPlaybackDelayed = false;
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        mPlaybackDelayed = false;
        playbackNow();
    } else if (res == AudioManager.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.
/** * A class to encapsulate information about an audio focus request. * An {@code AudioFocusRequest} instance is built by {@link Builder}, and is used to * request and abandon audio focus, respectively * with {@link AudioManager#requestAudioFocus(AudioFocusRequest)} and * {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}. * * <h3>What is audio focus?</h3> * <p>Audio focus is a concept introduced in API 8. It is used to convey the fact that a user can * only focus on a single audio stream at a time, e.g. listening to music or a podcast, but not * both at the same time. In some cases, multiple audio streams can be playing at the same time, * but there is only one the user would really listen to (focus on), while the other plays in * the background. An example of this is driving directions being spoken while music plays at * a reduced volume (a.k.a. ducking). * <p>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. * <p class="note">Note: applications should not play anything until granted focus.</p> * * <h3>The different types of focus requests</h3> * <p>There are four focus request types. A successful focus request with each will yield different * behaviors by the system and the other application that previously held audio focus. * <ul> * <li>{@link AudioManager#AUDIOFOCUS_GAIN} expresses the fact that your application is now the * sole source of audio that the user is listening to. The duration of the audio playback is * unknown, and is possibly very long: after the user finishes interacting with your application, * (s)he doesn’t expect another audio stream to resume. Examples of uses of this focus gain are * for music playback, for a game or a video player.</li> * * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT} is for a situation when you know your * application is temporarily grabbing focus from the current owner, but the user expects playback * to go back to where it was once your application no longer requires audio focus. An example is * for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will * time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events * ends, and if the user was listening to music when it started, the user expects music to resume, * but didn’t wish to listen to both at the same time.</li> * * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}: this focus request type is similar * to {@code AUDIOFOCUS_GAIN_TRANSIENT} for the temporary aspect of the focus request, but it also * expresses the fact during the time you own focus, you allow another application to keep playing * at a reduced volume, “ducked”. Examples are when playing driving directions or notifications, * it’s ok for music to keep playing, but not loud enough that it would prevent the directions to * be hard to understand. A typical attenuation by the “ducked” application is a factor of 0.2f * (or -14dB), that can for instance be applied with {@code MediaPlayer.setVolume(0.2f)} when * using this class for playback.</li> * * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} is also for a temporary request, * but also expresses that your application expects the device to not play anything else. This is * typically used if you are doing audio recording or speech recognition, and don’t want for * examples notifications to be played by the system during that time.</li> * </ul> * * <p>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 AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}. * * <h3>Qualifying your focus request</h3> * <h4>Use case requiring a focus request</h4> * <p>Any focus request is qualified by the {@link AudioAttributes} * (see {@link Builder#setAudioAttributes(AudioAttributes)}) that describe the audio use case that * will follow the request (once it's successful or granted). It is recommended to use the * same {@code AudioAttributes} for the request as the attributes you are using for audio/media * playback. * <br>If no attributes are set, default attributes of {@link AudioAttributes#USAGE_MEDIA} are used. * * <h4>Delayed focus</h4> * <p>Audio focus can be "locked" by the system for a number of reasons: during a phone call, when * the car to which the device is connected plays an emergency message... To support these * situations, the application can request to be notified when its request is fulfilled, by flagging * its request as accepting delayed focus, with {@link Builder#setAcceptsDelayedFocusGain(boolean)}. * <br>If focus is requested while being locked by the system, * {@link AudioManager#requestAudioFocus(AudioFocusRequest)} will return * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}. When focus isn't locked anymore, the focus * listener set with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener)} * or with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)} will * be called to notify the application it now owns audio focus. * * <h4>Pausing vs ducking</h4> * <p>When an application requested audio focus with * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus * owner. * <p class="note">Note: this behavior is <b>new for Android O</b>, 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}. * <p>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 * when it detects it would be ducking spoken content: such content is detected when the * {@code AudioAttributes} of the player are qualified by * {@link AudioAttributes#CONTENT_TYPE_SPEECH}. Refer for instance to * {@link AudioAttributes.Builder#setContentType(int)} and * {@link MediaPlayer#setAudioAttributes(AudioAttributes)} if you are writing a media playback * application for audio book, podcasts... Since the system will not automatically duck applications * that play speech, it calls their focus listener instead to notify them of * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, so they can pause instead. Note that * this behavior is independent of the use of {@code AudioFocusRequest}, but tied to the use * of {@code AudioAttributes}. * <p>If your application requires pausing instead of ducking for any other reason than playing * 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. * * <h4>Example</h4> * <p>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: * <ul> * <li>Creating {@code AudioAttributes} to be used for the playback and the focus request.</li> * <li>Configuring and creating the {@code AudioFocusRequest} instance that defines the intended * focus behaviors.</li> * <li>Requesting audio focus and checking the return code to see if playback can happen right * away, or is delayed.</li> * <li>Implementing a focus change listener to respond to focus gains and losses.</li> * </ul> * <p> * <pre class="prettyprint"> * // 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 == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { * mPlaybackDelayed = false; * } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { * mPlaybackDelayed = false; * playbackNow(); * } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { * mPlaybackDelayed = true; * } * } * * // implementation of the OnAudioFocusChangeListener * &#64;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. * </pre> */
public final class AudioFocusRequest { // default attributes for the request when not specified private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA).build();
@hide
/** @hide */
public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking"; private final OnAudioFocusChangeListener mFocusListener; // may be null private final Handler mListenerHandler; // may be null private final AudioAttributes mAttr; // never null private final int mFocusGain; private final int mFlags; private AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler, AudioAttributes attr, int focusGain, int flags) { mFocusListener = listener; mListenerHandler = handler; mFocusGain = focusGain; mAttr = attr; mFlags = flags; }
Params:
  • focusGain – value to check
@hide Checks whether a focus gain constant is a valid value for an audio focus request.
Returns:true if focusGain is a valid value for an audio focus request.
/** * @hide * Checks whether a focus gain constant is a valid value for an audio focus request. * @param focusGain value to check * @return true if focusGain is a valid value for an audio focus request. */
final static boolean isValidFocusGain(int focusGain) { switch (focusGain) { case AudioManager.AUDIOFOCUS_GAIN: case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: return true; default: return false; } }
@hide Returns the focus change listener set for this AudioFocusRequest.
Returns:null if no OnAudioFocusChangeListener was set.
/** * @hide * Returns the focus change listener set for this {@code AudioFocusRequest}. * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set. */
@TestApi public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() { return mFocusListener; }
@hide Returns the Handler to be used for the focus change listener.
Returns:the same Handler set in. Builder.setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler), or null if no listener was set.
/** * @hide * Returns the {@link Handler} to be used for the focus change listener. * @return the same {@code Handler} set in. * {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}, or null * if no listener was set. */
public @Nullable Handler getOnAudioFocusChangeListenerHandler() { return mListenerHandler; }
Returns the AudioAttributes set for this AudioFocusRequest, or the default attributes if none were set.
Returns:non-null AudioAttributes.
/** * Returns the {@link AudioAttributes} set for this {@code AudioFocusRequest}, or the default * attributes if none were set. * @return non-null {@link AudioAttributes}. */
public @NonNull AudioAttributes getAudioAttributes() { return mAttr; }
Returns the type of audio focus request configured for this AudioFocusRequest.
Returns:one of AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, and AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.
/** * Returns the type of audio focus request configured for this {@code AudioFocusRequest}. * @return one of {@link AudioManager#AUDIOFOCUS_GAIN}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. */
public int getFocusGain() { return mFocusGain; }
Returns whether the application that would use this AudioFocusRequest would pause when it is requested to duck.
Returns:the duck/pause behavior.
/** * Returns whether the application that would use this {@code AudioFocusRequest} would pause * when it is requested to duck. * @return the duck/pause behavior. */
public boolean willPauseWhenDucked() { return (mFlags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS; }
Returns whether the application that would use this AudioFocusRequest supports a focus gain granted after a temporary request failure.
Returns:whether delayed focus gain is supported.
/** * Returns whether the application that would use this {@code AudioFocusRequest} supports * a focus gain granted after a temporary request failure. * @return whether delayed focus gain is supported. */
public boolean acceptsDelayedFocusGain() { return (mFlags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == AudioManager.AUDIOFOCUS_FLAG_DELAY_OK; }
@hide Returns whether audio focus will be locked (i.e. focus cannot change) as a result of this focus request being successful.
Returns:whether this request will lock focus.
/** * @hide * Returns whether audio focus will be locked (i.e. focus cannot change) as a result of this * focus request being successful. * @return whether this request will lock focus. */
@SystemApi public boolean locksFocus() { return (mFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK; } int getFlags() { return mFlags; }
Builder class for AudioFocusRequest objects.

See 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
AudioAttributesattributes with usage set to AudioAttributes.USAGE_MEDIA
pauses on duckfalse
supports delayed focus grantfalse
/** * Builder class for {@link AudioFocusRequest} objects. * <p>See {@link AudioFocusRequest} for an example of building an instance with this builder. * <br>The default values for the instance to be built are: * <table> * <tr><td>focus listener and handler</td><td>none</td></tr> * <tr><td>{@code AudioAttributes}</td><td>attributes with usage set to * {@link AudioAttributes#USAGE_MEDIA}</td></tr> * <tr><td>pauses on duck</td><td>false</td></tr> * <tr><td>supports delayed focus grant</td><td>false</td></tr> * </table> */
public static final class Builder { private OnAudioFocusChangeListener mFocusListener; private Handler mListenerHandler; private AudioAttributes mAttr = FOCUS_DEFAULT_ATTR; private int mFocusGain; private boolean mPausesOnDuck = false; private boolean mDelayedFocus = false; private boolean mFocusLocked = false; private boolean mA11yForceDucking = false;
Constructs a new Builder, and specifies how audio focus will be requested. Valid values for focus requests are AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, and AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE.

By default there is no focus change listener, delayed focus is not supported, ducking is suitable for the application, and the AudioAttributes have a usage of AudioAttributes.USAGE_MEDIA.

Params:
  • focusGain – the type of audio focus gain that will be requested
Throws:
/** * Constructs a new {@code Builder}, and specifies how audio focus * will be requested. Valid values for focus requests are * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. * <p>By default there is no focus change listener, delayed focus is not supported, ducking * is suitable for the application, and the <code>AudioAttributes</code> * have a usage of {@link AudioAttributes#USAGE_MEDIA}. * @param focusGain the type of audio focus gain that will be requested * @throws IllegalArgumentException thrown when an invalid focus gain type is used */
public Builder(int focusGain) { setFocusGain(focusGain); }
Constructs a new Builder with all the properties of the AudioFocusRequest passed as parameter. Use this method when you want a new request to differ only by some properties.
Params:
  • requestToCopy – the non-null AudioFocusRequest to build a duplicate from.
Throws:
/** * Constructs a new {@code Builder} with all the properties of the {@code AudioFocusRequest} * passed as parameter. * Use this method when you want a new request to differ only by some properties. * @param requestToCopy the non-null {@code AudioFocusRequest} to build a duplicate from. * @throws IllegalArgumentException thrown when a null {@code AudioFocusRequest} is used. */
public Builder(@NonNull AudioFocusRequest requestToCopy) { if (requestToCopy == null) { throw new IllegalArgumentException("Illegal null AudioFocusRequest"); } mAttr = requestToCopy.mAttr; mFocusListener = requestToCopy.mFocusListener; mListenerHandler = requestToCopy.mListenerHandler; mFocusGain = requestToCopy.mFocusGain; mPausesOnDuck = requestToCopy.willPauseWhenDucked(); mDelayedFocus = requestToCopy.acceptsDelayedFocusGain(); }
Sets the type of focus gain that will be requested. Use this method to replace the focus gain when building a request by modifying an existing AudioFocusRequest instance.
Params:
  • focusGain – the type of audio focus gain that will be requested.
Throws:
Returns:this Builder instance
/** * Sets the type of focus gain that will be requested. * Use this method to replace the focus gain when building a request by modifying an * existing {@code AudioFocusRequest} instance. * @param focusGain the type of audio focus gain that will be requested. * @return this {@code Builder} instance * @throws IllegalArgumentException thrown when an invalid focus gain type is used */
public @NonNull Builder setFocusGain(int focusGain) { if (!isValidFocusGain(focusGain)) { throw new IllegalArgumentException("Illegal audio focus gain type " + focusGain); } mFocusGain = focusGain; return this; }
Sets the listener called when audio focus changes after being requested with AudioManager.requestAudioFocus(AudioFocusRequest), and until being abandoned with AudioManager.abandonAudioFocusRequest(AudioFocusRequest). Note that only focus changes (gains and losses) affecting the focus owner are reported, not gains and losses of other focus requesters in the system.
Notifications are delivered on the Looper associated with the one of the creation of the AudioManager used to request focus (see AudioManager.requestAudioFocus(AudioFocusRequest)).
Params:
  • listener – the listener receiving the focus change notifications.
Throws:
Returns:this Builder instance.
/** * Sets the listener called when audio focus changes after being requested with * {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned * with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}. * Note that only focus changes (gains and losses) affecting the focus owner are reported, * not gains and losses of other focus requesters in the system.<br> * Notifications are delivered on the {@link Looper} associated with the one of * the creation of the {@link AudioManager} used to request focus * (see {@link AudioManager#requestAudioFocus(AudioFocusRequest)}). * @param listener the listener receiving the focus change notifications. * @return this {@code Builder} instance. * @throws NullPointerException thrown when a null focus listener is used. */
public @NonNull Builder setOnAudioFocusChangeListener( @NonNull OnAudioFocusChangeListener listener) { if (listener == null) { throw new NullPointerException("Illegal null focus listener"); } mFocusListener = listener; mListenerHandler = null; return this; }
Params:
  • listener –
  • handler –
@hide Internal listener setter, no null checks on listener nor handler
Returns:this Builder instance.
/** * @hide * Internal listener setter, no null checks on listener nor handler * @param listener * @param handler * @return this {@code Builder} instance. */
@NonNull Builder setOnAudioFocusChangeListenerInt( OnAudioFocusChangeListener listener, Handler handler) { mFocusListener = listener; mListenerHandler = handler; return this; }
Sets the listener called when audio focus changes after being requested with AudioManager.requestAudioFocus(AudioFocusRequest), and until being abandoned with AudioManager.abandonAudioFocusRequest(AudioFocusRequest). Note that only focus changes (gains and losses) affecting the focus owner are reported, not gains and losses of other focus requesters in the system.
Params:
  • listener – the listener receiving the focus change notifications.
  • handler – the Handler for the thread on which to execute the notifications.
Throws:
Returns:this Builder instance.
/** * Sets the listener called when audio focus changes after being requested with * {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned * with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}. * Note that only focus changes (gains and losses) affecting the focus owner are reported, * not gains and losses of other focus requesters in the system. * @param listener the listener receiving the focus change notifications. * @param handler the {@link Handler} for the thread on which to execute * the notifications. * @return this {@code Builder} instance. * @throws NullPointerException thrown when a null focus listener or handler is used. */
public @NonNull Builder setOnAudioFocusChangeListener( @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) { if (listener == null || handler == null) { throw new NullPointerException("Illegal null focus listener or handler"); } mFocusListener = listener; mListenerHandler = handler; return this; }
Sets the AudioAttributes to be associated with the focus request, and which describe the use case for which focus is requested. As the focus requests typically precede audio playback, this information is used on certain platforms to declare the subsequent playback use case. It is therefore good practice to use in this method the same AudioAttributes as used for playback, see for example MediaPlayer.setAudioAttributes(AudioAttributes) in MediaPlayer or Builder.setAudioAttributes(AudioAttributes) in AudioTrack.
Params:
Throws:
Returns:this Builder instance.
/** * Sets the {@link AudioAttributes} to be associated with the focus request, and which * describe the use case for which focus is requested. * As the focus requests typically precede audio playback, this information is used on * certain platforms to declare the subsequent playback use case. It is therefore good * practice to use in this method the same {@code AudioAttributes} as used for * playback, see for example {@link MediaPlayer#setAudioAttributes(AudioAttributes)} in * {@code MediaPlayer} or {@link AudioTrack.Builder#setAudioAttributes(AudioAttributes)} * in {@code AudioTrack}. * @param attributes the {@link AudioAttributes} for the focus request. * @return this {@code Builder} instance. * @throws NullPointerException thrown when using null for the attributes. */
public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) { if (attributes == null) { throw new NullPointerException("Illegal null AudioAttributes"); } mAttr = attributes; return this; }
Declare the intended behavior of the application with regards to audio ducking. See more details in the AudioFocusRequest class documentation.
Params:
Returns:this Builder instance.
/** * Declare the intended behavior of the application with regards to audio ducking. * See more details in the {@link AudioFocusRequest} class documentation. * @param pauseOnDuck use {@code true} if the application intends to pause audio playback * when losing focus with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. * If {@code true}, note that you must also set a focus listener to receive such an * event, with * {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}. * @return this {@code Builder} instance. */
public @NonNull Builder setWillPauseWhenDucked(boolean pauseOnDuck) { mPausesOnDuck = pauseOnDuck; return this; }
Marks this focus request as compatible with delayed focus. See more details about delayed focus in the AudioFocusRequest class documentation.
Params:
Returns:this Builder instance
/** * Marks this focus request as compatible with delayed focus. * See more details about delayed focus in the {@link AudioFocusRequest} class * documentation. * @param acceptsDelayedFocusGain use {@code true} if the application supports delayed * focus. If {@code true}, note that you must also set a focus listener to be notified * of delayed focus gain, with * {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}. * @return this {@code Builder} instance */
public @NonNull Builder setAcceptsDelayedFocusGain(boolean acceptsDelayedFocusGain) { mDelayedFocus = acceptsDelayedFocusGain; return this; }
Params:
  • focusLocked – true when locking focus
@hide Marks this focus request as locking audio focus so granting is temporarily disabled. This feature can only be used by owners of a registered AudioPolicy in AudioManager.requestAudioFocus(AudioFocusRequest, AudioPolicy). Setting to false is the same as the default behavior.
Returns:this Builder instance
/** * @hide * Marks this focus request as locking audio focus so granting is temporarily disabled. * This feature can only be used by owners of a registered * {@link android.media.audiopolicy.AudioPolicy} in * {@link AudioManager#requestAudioFocus(AudioFocusRequest, android.media.audiopolicy.AudioPolicy)}. * Setting to false is the same as the default behavior. * @param focusLocked true when locking focus * @return this {@code Builder} instance */
@SystemApi public @NonNull Builder setLocksFocus(boolean focusLocked) { mFocusLocked = focusLocked; return this; }
Marks this focus request as forcing ducking, regardless of the conditions in which the system would or would not enforce ducking. Forcing ducking will only be honored when requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK with an AudioAttributes usage of AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY, coming from an accessibility service, and will be ignored otherwise.
Params:
  • forceDucking – true to force ducking
Returns:this Builder instance
/** * Marks this focus request as forcing ducking, regardless of the conditions in which * the system would or would not enforce ducking. * Forcing ducking will only be honored when requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK * with an {@link AudioAttributes} usage of * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, coming from an accessibility * service, and will be ignored otherwise. * @param forceDucking {@code true} to force ducking * @return this {@code Builder} instance */
public @NonNull Builder setForceDucking(boolean forceDucking) { mA11yForceDucking = forceDucking; return this; }
Builds a new AudioFocusRequest instance combining all the information gathered by this Builder's configuration methods.
Throws:
  • IllegalStateException – thrown when attempting to build a focus request that is set to accept delayed focus, or to pause on duck, but no focus change listener was set.
Returns:the AudioFocusRequest instance qualified by all the properties set on this Builder.
/** * Builds a new {@code AudioFocusRequest} instance combining all the information gathered * by this {@code Builder}'s configuration methods. * @return the {@code AudioFocusRequest} instance qualified by all the properties set * on this {@code Builder}. * @throws IllegalStateException thrown when attempting to build a focus request that is set * to accept delayed focus, or to pause on duck, but no focus change listener was set. */
public AudioFocusRequest build() { if ((mDelayedFocus || mPausesOnDuck) && (mFocusListener == null)) { throw new IllegalStateException( "Can't use delayed focus or pause on duck without a listener"); } if (mA11yForceDucking) { final Bundle extraInfo; if (mAttr.getBundle() == null) { extraInfo = new Bundle(); } else { extraInfo = mAttr.getBundle(); } // checking of usage and focus request is done server side extraInfo.putBoolean(KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING, true); mAttr = new AudioAttributes.Builder(mAttr).addBundle(extraInfo).build(); } final int flags = 0 | (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0) | (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0) | (mFocusLocked ? AudioManager.AUDIOFOCUS_FLAG_LOCK : 0); return new AudioFocusRequest(mFocusListener, mListenerHandler, mAttr, mFocusGain, flags); } } }