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