| /* |
| * 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.media.AudioManager.OnAudioFocusChangeListener; |
| import android.os.Handler; |
| import android.os.Looper; |
| |
| /** |
| * 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. |
| * <br><b>Note:<b> applications should not play anything until granted focus. |
| * |
| * <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 Builder#Builder(int)}, or with |
| * {@link Builder#setFocusGain(int)} after copying an existing instance with |
| * {@link Builder#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. Note that this behavior is <b>new for Android O<b>, whereas applications targeting SDK |
| * up to API 25, applications had to implement the ducking themselves when they received a focus |
| * loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. |
| * <br>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. |
| * |
| */ |
| 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(); |
| |
| 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; |
| } |
| |
| /** |
| * @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 {@code AudioFocusRequest}. |
| * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set. |
| */ |
| public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() { |
| return mFocusListener; |
| } |
| |
| /** |
| * @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 {@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 {@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 {@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 {@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. |
| * @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 {@link AudioFocusRequest} objects. |
| * <p> Here is an example where {@code Builder} is used to define the |
| * {@link AudioFocusRequest} for requesting audio focus: |
| * |
| * <pre class="prettyprint"> |
| * 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(); |
| * } |
| * </pre> |
| * |
| */ |
| 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; |
| |
| /** |
| * 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 {@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 {@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 |
| * {@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 main {@link Looper}. |
| * @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; |
| } |
| |
| /** |
| * @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 |
| * {@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 {@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 {@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 {@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; |
| } |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * 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"); |
| } |
| 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); |
| } |
| } |
| } |