• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.media.AudioManager.OnAudioFocusChangeListener;
23 import android.os.Handler;
24 import android.os.Looper;
25 
26 /**
27  * A class to encapsulate information about an audio focus request.
28  * An {@code AudioFocusRequest} instance is built by {@link Builder}, and is used to
29  * request and abandon audio focus, respectively
30  * with {@link AudioManager#requestAudioFocus(AudioFocusRequest)} and
31  * {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
32  *
33  * <h3>What is audio focus?</h3>
34  * <p>Audio focus is a concept introduced in API 8. It is used to convey the fact that a user can
35  * only focus on a single audio stream at a time, e.g. listening to music or a podcast, but not
36  * both at the same time. In some cases, multiple audio streams can be playing at the same time,
37  * but there is only one the user would really listen to (focus on), while the other plays in
38  * the background. An example of this is driving directions being spoken while music plays at
39  * a reduced volume (a.k.a. ducking).
40  * <p>When an application requests audio focus, it expresses its intention to “own” audio focus to
41  * play audio. Let’s review the different types of focus requests, the return value after a request,
42  * and the responses to a loss.
43  * <p class="note">Note: applications should not play anything until granted focus.</p>
44  *
45  * <h3>The different types of focus requests</h3>
46  * <p>There are four focus request types. A successful focus request with each will yield different
47  * behaviors by the system and the other application that previously held audio focus.
48  * <ul>
49  * <li>{@link AudioManager#AUDIOFOCUS_GAIN} expresses the fact that your application is now the
50  * sole source of audio that the user is listening to. The duration of the audio playback is
51  * unknown, and is possibly very long: after the user finishes interacting with your application,
52  * (s)he doesn’t expect another audio stream to resume. Examples of uses of this focus gain are
53  * for music playback, for a game or a video player.</li>
54  *
55  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT} is for a situation when you know your
56  * application is temporarily grabbing focus from the current owner, but the user expects playback
57  * to go back to where it was once your application no longer requires audio focus. An example is
58  * for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will
59  * time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events
60  * ends, and if the user was listening to music when it started, the user expects music to resume,
61  * but didn’t wish to listen to both at the same time.</li>
62  *
63  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}: this focus request type is similar
64  * to {@code AUDIOFOCUS_GAIN_TRANSIENT} for the temporary aspect of the focus request, but it also
65  * expresses the fact during the time you own focus, you allow another application to keep playing
66  * at a reduced volume, “ducked”. Examples are when playing driving directions or notifications,
67  * it’s ok for music to keep playing, but not loud enough that it would prevent the directions to
68  * be hard to understand. A typical attenuation by the “ducked” application is a factor of 0.2f
69  * (or -14dB), that can for instance be applied with {@code MediaPlayer.setVolume(0.2f)} when
70  * using this class for playback.</li>
71  *
72  * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} is also for a temporary request,
73  * but also expresses that your application expects the device to not play anything else. This is
74  * typically used if you are doing audio recording or speech recognition, and don’t want for
75  * examples notifications to be played by the system during that time.</li>
76  * </ul>
77  *
78  * <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests
79  * explained above. It is passed when building an {@code AudioFocusRequest} instance with its
80  * builder in the {@link Builder} constructor
81  * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or
82  * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with
83  * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}.
84  *
85  * <h3>Qualifying your focus request</h3>
86  * <h4>Use case requiring a focus request</h4>
87  * <p>Any focus request is qualified by the {@link AudioAttributes}
88  * (see {@link Builder#setAudioAttributes(AudioAttributes)}) that describe the audio use case that
89  * will follow the request (once it's successful or granted). It is recommended to use the
90  * same {@code AudioAttributes} for the request as the attributes you are using for audio/media
91  * playback.
92  * <br>If no attributes are set, default attributes of {@link AudioAttributes#USAGE_MEDIA} are used.
93  *
94  * <h4>Delayed focus</h4>
95  * <p>Audio focus can be "locked" by the system for a number of reasons: during a phone call, when
96  * the car to which the device is connected plays an emergency message... To support these
97  * situations, the application can request to be notified when its request is fulfilled, by flagging
98  * its request as accepting delayed focus, with {@link Builder#setAcceptsDelayedFocusGain(boolean)}.
99  * <br>If focus is requested while being locked by the system,
100  * {@link AudioManager#requestAudioFocus(AudioFocusRequest)} will return
101  * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}. When focus isn't locked anymore, the focus
102  * listener set with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener)}
103  * or with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)} will
104  * be called to notify the application it now owns audio focus.
105  *
106  * <h4>Pausing vs ducking</h4>
107  * <p>When an application requested audio focus with
108  * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus
109  * owner.
110  * <p class="note">Note: this behavior is <b>new for Android O</b>, whereas applications targeting
111  * SDK level up to API 25 had to implement the ducking themselves when they received a focus
112  * loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
113  * <p>But ducking is not always the behavior expected by the user. A typical example is when the
114  * device plays driving directions while the user is listening to an audio book or podcast, and
115  * expects the audio playback to pause, instead of duck, as it is hard to understand a navigation
116  * prompt and spoken content at the same time. Therefore the system will not automatically duck
117  * when it detects it would be ducking spoken content: such content is detected when the
118  * {@code AudioAttributes} of the player are qualified by
119  * {@link AudioAttributes#CONTENT_TYPE_SPEECH}. Refer for instance to
120  * {@link AudioAttributes.Builder#setContentType(int)} and
121  * {@link MediaPlayer#setAudioAttributes(AudioAttributes)} if you are writing a media playback
122  * application for audio book, podcasts... Since the system will not automatically duck applications
123  * that play speech, it calls their focus listener instead to notify them of
124  * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, so they can pause instead. Note that
125  * this behavior is independent of the use of {@code AudioFocusRequest}, but tied to the use
126  * of {@code AudioAttributes}.
127  * <p>If your application requires pausing instead of ducking for any other reason than playing
128  * speech, you can also declare so with {@link Builder#setWillPauseWhenDucked(boolean)}, which will
129  * cause the system to call your focus listener instead of automatically ducking.
130  *
131  * <h4>Example</h4>
132  * <p>The example below covers the following steps to be found in any application that would play
133  * audio, and use audio focus. Here we play an audio book, and our application is intended to pause
134  * rather than duck when it loses focus. These steps consist in:
135  * <ul>
136  * <li>Creating {@code AudioAttributes} to be used for the playback and the focus request.</li>
137  * <li>Configuring and creating the {@code AudioFocusRequest} instance that defines the intended
138  *     focus behaviors.</li>
139  * <li>Requesting audio focus and checking the return code to see if playback can happen right
140  *     away, or is delayed.</li>
141  * <li>Implementing a focus change listener to respond to focus gains and losses.</li>
142  * </ul>
143  * <p>
144  * <pre class="prettyprint">
145  * // initialization of the audio attributes and focus request
146  * mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
147  * mPlaybackAttributes = new AudioAttributes.Builder()
148  *         .setUsage(AudioAttributes.USAGE_MEDIA)
149  *         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
150  *         .build();
151  * mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
152  *         .setAudioAttributes(mPlaybackAttributes)
153  *         .setAcceptsDelayedFocusGain(true)
154  *         .setWillPauseWhenDucked(true)
155  *         .setOnAudioFocusChangeListener(this, mMyHandler)
156  *         .build();
157  * mMediaPlayer = new MediaPlayer();
158  * mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
159  * final Object mFocusLock = new Object();
160  *
161  * boolean mPlaybackDelayed = false;
162  *
163  * // requesting audio focus
164  * int res = mAudioManager.requestAudioFocus(mFocusRequest);
165  * synchronized (mFocusLock) {
166  *     if (res == AUDIOFOCUS_REQUEST_FAILED) {
167  *         mPlaybackDelayed = false;
168  *     } else if (res == AUDIOFOCUS_REQUEST_GRANTED) {
169  *         mPlaybackDelayed = false;
170  *         playbackNow();
171  *     } else if (res == AUDIOFOCUS_REQUEST_DELAYED) {
172  *        mPlaybackDelayed = true;
173  *     }
174  * }
175  *
176  * // implementation of the OnAudioFocusChangeListener
177  * &#64;Override
178  * public void onAudioFocusChange(int focusChange) {
179  *     switch (focusChange) {
180  *         case AudioManager.AUDIOFOCUS_GAIN:
181  *             if (mPlaybackDelayed || mResumeOnFocusGain) {
182  *                 synchronized (mFocusLock) {
183  *                     mPlaybackDelayed = false;
184  *                     mResumeOnFocusGain = false;
185  *                 }
186  *                 playbackNow();
187  *             }
188  *             break;
189  *         case AudioManager.AUDIOFOCUS_LOSS:
190  *             synchronized (mFocusLock) {
191  *                 // this is not a transient loss, we shouldn't automatically resume for now
192  *                 mResumeOnFocusGain = false;
193  *                 mPlaybackDelayed = false;
194  *             }
195  *             pausePlayback();
196  *             break;
197  *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
198  *         case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
199  *             // we handle all transient losses the same way because we never duck audio books
200  *             synchronized (mFocusLock) {
201  *                 // we should only resume if playback was interrupted
202  *                 mResumeOnFocusGain = mMediaPlayer.isPlaying();
203  *                 mPlaybackDelayed = false;
204  *             }
205  *             pausePlayback();
206  *             break;
207  *     }
208  * }
209  *
210  * // Important:
211  * // Also set "mResumeOnFocusGain" to false when the user pauses or stops playback: this way your
212  * // application doesn't automatically restart when it gains focus, even though the user had
213  * // stopped it.
214  * </pre>
215  */
216 
217 public final class AudioFocusRequest {
218 
219     // default attributes for the request when not specified
220     private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
221             .setUsage(AudioAttributes.USAGE_MEDIA).build();
222 
223     private final OnAudioFocusChangeListener mFocusListener; // may be null
224     private final Handler mListenerHandler;                  // may be null
225     private final AudioAttributes mAttr;                     // never null
226     private final int mFocusGain;
227     private final int mFlags;
228 
AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler, AudioAttributes attr, int focusGain, int flags)229     private AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler,
230             AudioAttributes attr, int focusGain, int flags) {
231         mFocusListener = listener;
232         mListenerHandler = handler;
233         mFocusGain = focusGain;
234         mAttr = attr;
235         mFlags = flags;
236     }
237 
238     /**
239      * @hide
240      * Checks whether a focus gain constant is a valid value for an audio focus request.
241      * @param focusGain value to check
242      * @return true if focusGain is a valid value for an audio focus request.
243      */
isValidFocusGain(int focusGain)244     final static boolean isValidFocusGain(int focusGain) {
245         switch (focusGain) {
246             case AudioManager.AUDIOFOCUS_GAIN:
247             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
248             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
249             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
250                 return true;
251             default:
252                 return false;
253         }
254     }
255 
256     /**
257      * @hide
258      * Returns the focus change listener set for this {@code AudioFocusRequest}.
259      * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
260      */
getOnAudioFocusChangeListener()261     public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
262         return mFocusListener;
263     }
264 
265     /**
266      * @hide
267      * Returns the {@link Handler} to be used for the focus change listener.
268      * @return the same {@code Handler} set in.
269      *   {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}, or null
270      *   if no listener was set.
271      */
getOnAudioFocusChangeListenerHandler()272     public @Nullable Handler getOnAudioFocusChangeListenerHandler() {
273         return mListenerHandler;
274     }
275 
276     /**
277      * Returns the {@link AudioAttributes} set for this {@code AudioFocusRequest}, or the default
278      * attributes if none were set.
279      * @return non-null {@link AudioAttributes}.
280      */
getAudioAttributes()281     public @NonNull AudioAttributes getAudioAttributes() {
282         return mAttr;
283     }
284 
285     /**
286      * Returns the type of audio focus request configured for this {@code AudioFocusRequest}.
287      * @return one of {@link AudioManager#AUDIOFOCUS_GAIN},
288      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
289      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
290      * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
291      */
getFocusGain()292     public int getFocusGain() {
293         return mFocusGain;
294     }
295 
296     /**
297      * Returns whether the application that would use this {@code AudioFocusRequest} would pause
298      * when it is requested to duck.
299      * @return the duck/pause behavior.
300      */
willPauseWhenDucked()301     public boolean willPauseWhenDucked() {
302         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
303                 == AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
304     }
305 
306     /**
307      * Returns whether the application that would use this {@code AudioFocusRequest} supports
308      * a focus gain granted after a temporary request failure.
309      * @return whether delayed focus gain is supported.
310      */
acceptsDelayedFocusGain()311     public boolean acceptsDelayedFocusGain() {
312         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK)
313                 == AudioManager.AUDIOFOCUS_FLAG_DELAY_OK;
314     }
315 
316     /**
317      * @hide
318      * Returns whether audio focus will be locked (i.e. focus cannot change) as a result of this
319      * focus request being successful.
320      * @return whether this request will lock focus.
321      */
322     @SystemApi
locksFocus()323     public boolean locksFocus() {
324         return (mFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK)
325                 == AudioManager.AUDIOFOCUS_FLAG_LOCK;
326     }
327 
getFlags()328     int getFlags() {
329         return mFlags;
330     }
331 
332     /**
333      * Builder class for {@link AudioFocusRequest} objects.
334      * <p>See {@link AudioFocusRequest} for an example of building an instance with this builder.
335      * <br>The default values for the instance to be built are:
336      * <table>
337      * <tr><td>focus listener and handler</td><td>none</td></tr>
338      * <tr><td>{@code AudioAttributes}</td><td>attributes with usage set to
339      *     {@link AudioAttributes#USAGE_MEDIA}</td></tr>
340      * <tr><td>pauses on duck</td><td>false</td></tr>
341      * <tr><td>supports delayed focus grant</td><td>false</td></tr>
342      * </table>
343      */
344     public static final class Builder {
345         private OnAudioFocusChangeListener mFocusListener;
346         private Handler mListenerHandler;
347         private AudioAttributes mAttr = FOCUS_DEFAULT_ATTR;
348         private int mFocusGain;
349         private boolean mPausesOnDuck = false;
350         private boolean mDelayedFocus = false;
351         private boolean mFocusLocked = false;
352 
353         /**
354          * Constructs a new {@code Builder}, and specifies how audio focus
355          * will be requested. Valid values for focus requests are
356          * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT},
357          * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and
358          * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}.
359          * <p>By default there is no focus change listener, delayed focus is not supported, ducking
360          * is suitable for the application, and the <code>AudioAttributes</code>
361          * have a usage of {@link AudioAttributes#USAGE_MEDIA}.
362          * @param focusGain the type of audio focus gain that will be requested
363          * @throws IllegalArgumentException thrown when an invalid focus gain type is used
364          */
Builder(int focusGain)365         public Builder(int focusGain) {
366             setFocusGain(focusGain);
367         }
368 
369         /**
370          * Constructs a new {@code Builder} with all the properties of the {@code AudioFocusRequest}
371          * passed as parameter.
372          * Use this method when you want a new request to differ only by some properties.
373          * @param requestToCopy the non-null {@code AudioFocusRequest} to build a duplicate from.
374          * @throws IllegalArgumentException thrown when a null {@code AudioFocusRequest} is used.
375          */
Builder(@onNull AudioFocusRequest requestToCopy)376         public Builder(@NonNull AudioFocusRequest requestToCopy) {
377             if (requestToCopy == null) {
378                 throw new IllegalArgumentException("Illegal null AudioFocusRequest");
379             }
380             mAttr = requestToCopy.mAttr;
381             mFocusListener = requestToCopy.mFocusListener;
382             mListenerHandler = requestToCopy.mListenerHandler;
383             mFocusGain = requestToCopy.mFocusGain;
384             mPausesOnDuck = requestToCopy.willPauseWhenDucked();
385             mDelayedFocus = requestToCopy.acceptsDelayedFocusGain();
386         }
387 
388         /**
389          * Sets the type of focus gain that will be requested.
390          * Use this method to replace the focus gain when building a request by modifying an
391          * existing {@code AudioFocusRequest} instance.
392          * @param focusGain the type of audio focus gain that will be requested.
393          * @return this {@code Builder} instance
394          * @throws IllegalArgumentException thrown when an invalid focus gain type is used
395          */
setFocusGain(int focusGain)396         public @NonNull Builder setFocusGain(int focusGain) {
397             if (!isValidFocusGain(focusGain)) {
398                 throw new IllegalArgumentException("Illegal audio focus gain type " + focusGain);
399             }
400             mFocusGain = focusGain;
401             return this;
402         }
403 
404         /**
405          * Sets the listener called when audio focus changes after being requested with
406          *   {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned
407          *   with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
408          *   Note that only focus changes (gains and losses) affecting the focus owner are reported,
409          *   not gains and losses of other focus requesters in the system.<br>
410          *   Notifications are delivered on the main {@link Looper}.
411          * @param listener the listener receiving the focus change notifications.
412          * @return this {@code Builder} instance.
413          * @throws NullPointerException thrown when a null focus listener is used.
414          */
setOnAudioFocusChangeListener( @onNull OnAudioFocusChangeListener listener)415         public @NonNull Builder setOnAudioFocusChangeListener(
416                 @NonNull OnAudioFocusChangeListener listener) {
417             if (listener == null) {
418                 throw new NullPointerException("Illegal null focus listener");
419             }
420             mFocusListener = listener;
421             mListenerHandler = null;
422             return this;
423         }
424 
425         /**
426          * @hide
427          * Internal listener setter, no null checks on listener nor handler
428          * @param listener
429          * @param handler
430          * @return this {@code Builder} instance.
431          */
setOnAudioFocusChangeListenerInt( OnAudioFocusChangeListener listener, Handler handler)432         @NonNull Builder setOnAudioFocusChangeListenerInt(
433                 OnAudioFocusChangeListener listener, Handler handler) {
434             mFocusListener = listener;
435             mListenerHandler = handler;
436             return this;
437         }
438 
439         /**
440          * Sets the listener called when audio focus changes after being requested with
441          *   {@link AudioManager#requestAudioFocus(AudioFocusRequest)}, and until being abandoned
442          *   with {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}.
443          *   Note that only focus changes (gains and losses) affecting the focus owner are reported,
444          *   not gains and losses of other focus requesters in the system.
445          * @param listener the listener receiving the focus change notifications.
446          * @param handler the {@link Handler} for the thread on which to execute
447          *   the notifications.
448          * @return this {@code Builder} instance.
449          * @throws NullPointerException thrown when a null focus listener or handler is used.
450          */
setOnAudioFocusChangeListener( @onNull OnAudioFocusChangeListener listener, @NonNull Handler handler)451         public @NonNull Builder setOnAudioFocusChangeListener(
452                 @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) {
453             if (listener == null || handler == null) {
454                 throw new NullPointerException("Illegal null focus listener or handler");
455             }
456             mFocusListener = listener;
457             mListenerHandler = handler;
458             return this;
459         }
460 
461         /**
462          * Sets the {@link AudioAttributes} to be associated with the focus request, and which
463          * describe the use case for which focus is requested.
464          * As the focus requests typically precede audio playback, this information is used on
465          * certain platforms to declare the subsequent playback use case. It is therefore good
466          * practice to use in this method the same {@code AudioAttributes} as used for
467          * playback, see for example {@link MediaPlayer#setAudioAttributes(AudioAttributes)} in
468          * {@code MediaPlayer} or {@link AudioTrack.Builder#setAudioAttributes(AudioAttributes)}
469          * in {@code AudioTrack}.
470          * @param attributes the {@link AudioAttributes} for the focus request.
471          * @return this {@code Builder} instance.
472          * @throws NullPointerException thrown when using null for the attributes.
473          */
setAudioAttributes(@onNull AudioAttributes attributes)474         public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) {
475             if (attributes == null) {
476                 throw new NullPointerException("Illegal null AudioAttributes");
477             }
478             mAttr = attributes;
479             return this;
480         }
481 
482         /**
483          * Declare the intended behavior of the application with regards to audio ducking.
484          * See more details in the {@link AudioFocusRequest} class documentation.
485          * @param pauseOnDuck use {@code true} if the application intends to pause audio playback
486          *    when losing focus with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
487          *    If {@code true}, note that you must also set a focus listener to receive such an
488          *    event, with
489          *    {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
490          * @return this {@code Builder} instance.
491          */
setWillPauseWhenDucked(boolean pauseOnDuck)492         public @NonNull Builder setWillPauseWhenDucked(boolean pauseOnDuck) {
493             mPausesOnDuck = pauseOnDuck;
494             return this;
495         }
496 
497         /**
498          * Marks this focus request as compatible with delayed focus.
499          * See more details about delayed focus in the {@link AudioFocusRequest} class
500          * documentation.
501          * @param acceptsDelayedFocusGain use {@code true} if the application supports delayed
502          *    focus. If {@code true}, note that you must also set a focus listener to be notified
503          *    of delayed focus gain, with
504          *    {@link #setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)}.
505          * @return this {@code Builder} instance
506          */
setAcceptsDelayedFocusGain(boolean acceptsDelayedFocusGain)507         public @NonNull Builder setAcceptsDelayedFocusGain(boolean acceptsDelayedFocusGain) {
508             mDelayedFocus = acceptsDelayedFocusGain;
509             return this;
510         }
511 
512         /**
513          * @hide
514          * Marks this focus request as locking audio focus so granting is temporarily disabled.
515          * This feature can only be used by owners of a registered
516          * {@link android.media.audiopolicy.AudioPolicy} in
517          * {@link AudioManager#requestAudioFocus(AudioFocusRequest, android.media.audiopolicy.AudioPolicy)}.
518          * Setting to false is the same as the default behavior.
519          * @param focusLocked true when locking focus
520          * @return this {@code Builder} instance
521          */
522         @SystemApi
setLocksFocus(boolean focusLocked)523         public @NonNull Builder setLocksFocus(boolean focusLocked) {
524             mFocusLocked = focusLocked;
525             return this;
526         }
527 
528         /**
529          * Builds a new {@code AudioFocusRequest} instance combining all the information gathered
530          * by this {@code Builder}'s configuration methods.
531          * @return the {@code AudioFocusRequest} instance qualified by all the properties set
532          *   on this {@code Builder}.
533          * @throws IllegalStateException thrown when attempting to build a focus request that is set
534          *    to accept delayed focus, or to pause on duck, but no focus change listener was set.
535          */
build()536         public AudioFocusRequest build() {
537             if ((mDelayedFocus || mPausesOnDuck) && (mFocusListener == null)) {
538                 throw new IllegalStateException(
539                         "Can't use delayed focus or pause on duck without a listener");
540             }
541             final int flags = 0
542                     | (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0)
543                     | (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0)
544                     | (mFocusLocked  ? AudioManager.AUDIOFOCUS_FLAG_LOCK : 0);
545             return new AudioFocusRequest(mFocusListener, mListenerHandler,
546                     mAttr, mFocusGain, flags);
547         }
548     }
549 }
550