Files
frameworks_base/media/java/android/media/AudioFocusRequest.java
Jean-Michel Trivi d043c3aba0 AudioFocusRequest: getOnAudioFocusChangeListener in test API
Bug: 77651038
Test: CTS AudioFocusTest
Change-Id: I071057f1ed0ed6c56c16182a48191d763d408ae1
2018-04-06 16:59:21 -07:00

583 lines
28 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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. Lets 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 doesnt 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 didnt 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,
* its 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 dont want for
* examples notifications to be played by the system during that time.</li>
* </ul>
*
* <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests
* explained above. It is passed when building an {@code AudioFocusRequest} instance with its
* builder in the {@link Builder} constructor
* {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or
* with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with
* {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}.
*
* <h3>Qualifying your focus request</h3>
* <h4>Use case requiring a focus request</h4>
* <p>Any focus request is qualified by the {@link AudioAttributes}
* (see {@link Builder#setAudioAttributes(AudioAttributes)}) that describe the audio use case that
* will follow the request (once it's successful or granted). It is recommended to use the
* same {@code AudioAttributes} for the request as the attributes you are using for audio/media
* playback.
* <br>If no attributes are set, default attributes of {@link AudioAttributes#USAGE_MEDIA} are used.
*
* <h4>Delayed focus</h4>
* <p>Audio focus can be "locked" by the system for a number of reasons: during a phone call, when
* the car to which the device is connected plays an emergency message... To support these
* situations, the application can request to be notified when its request is fulfilled, by flagging
* its request as accepting delayed focus, with {@link Builder#setAcceptsDelayedFocusGain(boolean)}.
* <br>If focus is requested while being locked by the system,
* {@link AudioManager#requestAudioFocus(AudioFocusRequest)} will return
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}. When focus isn't locked anymore, the focus
* listener set with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener)}
* or with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)} will
* be called to notify the application it now owns audio focus.
*
* <h4>Pausing vs ducking</h4>
* <p>When an application requested audio focus with
* {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus
* owner.
* <p class="note">Note: this behavior is <b>new for Android O</b>, whereas applications targeting
* SDK level up to API 25 had to implement the ducking themselves when they received a focus
* loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
* <p>But ducking is not always the behavior expected by the user. A typical example is when the
* device plays driving directions while the user is listening to an audio book or podcast, and
* expects the audio playback to pause, instead of duck, as it is hard to understand a navigation
* prompt and spoken content at the same time. Therefore the system will not automatically duck
* when it detects it would be ducking spoken content: such content is detected when the
* {@code AudioAttributes} of the player are qualified by
* {@link AudioAttributes#CONTENT_TYPE_SPEECH}. Refer for instance to
* {@link AudioAttributes.Builder#setContentType(int)} and
* {@link MediaPlayer#setAudioAttributes(AudioAttributes)} if you are writing a media playback
* application for audio book, podcasts... Since the system will not automatically duck applications
* that play speech, it calls their focus listener instead to notify them of
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, so they can pause instead. Note that
* this behavior is independent of the use of {@code AudioFocusRequest}, but tied to the use
* of {@code AudioAttributes}.
* <p>If your application requires pausing instead of ducking for any other reason than playing
* speech, you can also declare so with {@link Builder#setWillPauseWhenDucked(boolean)}, which will
* cause the system to call your focus listener instead of automatically ducking.
*
* <h4>Example</h4>
* <p>The example below covers the following steps to be found in any application that would play
* audio, and use audio focus. Here we play an audio book, and our application is intended to pause
* rather than duck when it loses focus. These steps consist in:
* <ul>
* <li>Creating {@code AudioAttributes} to be used for the playback and the focus request.</li>
* <li>Configuring and creating the {@code AudioFocusRequest} instance that defines the intended
* focus behaviors.</li>
* <li>Requesting audio focus and checking the return code to see if playback can happen right
* away, or is delayed.</li>
* <li>Implementing a focus change listener to respond to focus gains and losses.</li>
* </ul>
* <p>
* <pre class="prettyprint">
* // initialization of the audio attributes and focus request
* mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
* mPlaybackAttributes = new AudioAttributes.Builder()
* .setUsage(AudioAttributes.USAGE_MEDIA)
* .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
* .build();
* mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
* .setAudioAttributes(mPlaybackAttributes)
* .setAcceptsDelayedFocusGain(true)
* .setWillPauseWhenDucked(true)
* .setOnAudioFocusChangeListener(this, mMyHandler)
* .build();
* mMediaPlayer = new MediaPlayer();
* mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
* final Object mFocusLock = new Object();
*
* boolean mPlaybackDelayed = false;
*
* // requesting audio focus
* int res = mAudioManager.requestAudioFocus(mFocusRequest);
* synchronized (mFocusLock) {
* if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
* mPlaybackDelayed = false;
* } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
* mPlaybackDelayed = false;
* playbackNow();
* } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
* mPlaybackDelayed = true;
* }
* }
*
* // implementation of the OnAudioFocusChangeListener
* &#64;Override
* public void onAudioFocusChange(int focusChange) {
* switch (focusChange) {
* case AudioManager.AUDIOFOCUS_GAIN:
* if (mPlaybackDelayed || mResumeOnFocusGain) {
* synchronized (mFocusLock) {
* mPlaybackDelayed = false;
* mResumeOnFocusGain = false;
* }
* playbackNow();
* }
* break;
* case AudioManager.AUDIOFOCUS_LOSS:
* synchronized (mFocusLock) {
* // this is not a transient loss, we shouldn't automatically resume for now
* mResumeOnFocusGain = false;
* mPlaybackDelayed = false;
* }
* pausePlayback();
* break;
* case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
* case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
* // we handle all transient losses the same way because we never duck audio books
* synchronized (mFocusLock) {
* // we should only resume if playback was interrupted
* mResumeOnFocusGain = mMediaPlayer.isPlaying();
* mPlaybackDelayed = false;
* }
* pausePlayback();
* break;
* }
* }
*
* // Important:
* // Also set "mResumeOnFocusGain" to false when the user pauses or stops playback: this way your
* // application doesn't automatically restart when it gains focus, even though the user had
* // stopped it.
* </pre>
*/
public final class AudioFocusRequest {
// default attributes for the request when not specified
private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA).build();
/** @hide */
public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking";
private final OnAudioFocusChangeListener mFocusListener; // may be null
private final Handler mListenerHandler; // may be null
private final AudioAttributes mAttr; // never null
private final int mFocusGain;
private final int mFlags;
private AudioFocusRequest(OnAudioFocusChangeListener listener, Handler handler,
AudioAttributes attr, int focusGain, int flags) {
mFocusListener = listener;
mListenerHandler = handler;
mFocusGain = focusGain;
mAttr = attr;
mFlags = flags;
}
/**
* @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 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;
}
/**
* 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);
}
}
}