|  | /* | 
|  | * 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 {@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#Builder(int)}, or | 
|  | * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with | 
|  | * {@link AudioFocusRequest.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. | 
|  | * <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 | 
|  | * @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 */ | 
|  | public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking"; | 
|  |  | 
|  | private final @Nullable OnAudioFocusChangeListener mFocusListener; | 
|  | private final @Nullable Handler mListenerHandler; | 
|  | private final @NonNull AudioAttributes mAttr; | 
|  | 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. | 
|  | */ | 
|  | @TestApi | 
|  | 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>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 {@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 {@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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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 {@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); | 
|  | } | 
|  | } | 
|  | } |