diff options
author | 2017-05-01 06:49:06 +0000 | |
---|---|---|
committer | 2017-05-01 06:49:06 +0000 | |
commit | 926d4a4d49838e0924da4a3dac6e504a7b451103 (patch) | |
tree | 1feaf4ec1f4dae029e4d4d51fd8e74bb6860d60f | |
parent | 08117e0308f7b341d72942266d0916a4de35f02b (diff) | |
parent | 5642a6559b20dd72fc5af187c08ad3a68b595f03 (diff) |
Merge "AudioFocusRequest: more docs, listener without handler" into oc-dev am: 128f8aa8e3
am: 5642a6559b
Change-Id: I37a1a943406ec2384bc403f70b790a56f0fd00d0
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | api/test-current.txt | 1 | ||||
-rw-r--r-- | media/java/android/media/AudioFocusRequest.java | 140 |
4 files changed, 127 insertions, 16 deletions
diff --git a/api/current.txt b/api/current.txt index c0e0a770287c..90da660cbdd2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21024,6 +21024,7 @@ package android.media { method public android.media.AudioFocusRequest.Builder setAcceptsDelayedFocusGain(boolean); method public android.media.AudioFocusRequest.Builder setAudioAttributes(android.media.AudioAttributes); method public android.media.AudioFocusRequest.Builder setFocusGain(int); + method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener); method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler); method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean); } diff --git a/api/system-current.txt b/api/system-current.txt index 5faac780d0d0..9f696d2cb140 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -22835,6 +22835,7 @@ package android.media { method public android.media.AudioFocusRequest.Builder setAudioAttributes(android.media.AudioAttributes); method public android.media.AudioFocusRequest.Builder setFocusGain(int); method public android.media.AudioFocusRequest.Builder setLocksFocus(boolean); + method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener); method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler); method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean); } diff --git a/api/test-current.txt b/api/test-current.txt index bfd722aaf092..2ac284efbbd7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -21132,6 +21132,7 @@ package android.media { method public android.media.AudioFocusRequest.Builder setAcceptsDelayedFocusGain(boolean); method public android.media.AudioFocusRequest.Builder setAudioAttributes(android.media.AudioAttributes); method public android.media.AudioFocusRequest.Builder setFocusGain(int); + method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener); method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler); method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean); } diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java index 2e4d199e74ce..29d19860eba3 100644 --- a/media/java/android/media/AudioFocusRequest.java +++ b/media/java/android/media/AudioFocusRequest.java @@ -29,14 +29,104 @@ import android.os.Looper; * request and abandon audio focus, respectively * with {@link AudioManager#requestAudioFocus(AudioFocusRequest)} and * {@link AudioManager#abandonAudioFocusRequest(AudioFocusRequest)}. - * <p>In the context of describing audio focus, the term "ducking" is used. It describes a temporary - * lowering of the audio level of an application in response to another application playing audio - * concurrently. An example is during the playback of driving directions, - * a user listening to music expects the music to "duck" during the playback of the message - * announcing directions. + * + * <h3>What is audio focus?</h3> + * <p>Audio focus is a concept introduced in API 8. It is used to convey the fact that a user can + * only focus on a single audio stream at a time, e.g. listening to music or a podcast, but not + * both at the same time. In some cases, multiple audio streams can be playing at the same time, + * but there is only one the user would really listen to (focus on), while the other plays in + * the background. An example of this is driving directions being spoken while music plays at + * a reduced volume (a.k.a. ducking). + * <p>When an application requests audio focus, it expresses its intention to “own” audio focus to + * play audio. Let’s review the different types of focus requests, the return value after a request, + * and the responses to a loss. + * <br><b>Note:<b> applications should not play anything until granted focus. + * + * <h3>The different types of focus requests</h3> + * <p>There are four focus request types. A successful focus request with each will yield different + * behaviors by the system and the other application that previously held audio focus. + * <ul> + * <li>{@link AudioManager#AUDIOFOCUS_GAIN} expresses the fact that your application is now the + * sole source of audio that the user is listening to. The duration of the audio playback is + * unknown, and is possibly very long: after the user finishes interacting with your application, + * (s)he doesn’t expect another audio stream to resume. Examples of uses of this focus gain are + * for music playback, for a game or a video player.</li> + * + * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT} is for a situation when you know your + * application is temporarily grabbing focus from the current owner, but the user expects playback + * to go back to where it was once your application no longer requires audio focus. An example is + * for playing an alarm, or during a VoIP call. The playback is known to be finite: the alarm will + * time-out or be dismissed, the VoIP call has a beginning and an end. When any of those events + * ends, and if the user was listening to music when it started, the user expects music to resume, + * but didn’t wish to listen to both at the same time.</li> + * + * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}: this focus request type is similar + * to {@code AUDIOFOCUS_GAIN_TRANSIENT} for the temporary aspect of the focus request, but it also + * expresses the fact during the time you own focus, you allow another application to keep playing + * at a reduced volume, “ducked”. Examples are when playing driving directions or notifications, + * it’s ok for music to keep playing, but not loud enough that it would prevent the directions to + * be hard to understand. A typical attenuation by the “ducked” application is a factor of 0.2f + * (or -14dB), that can for instance be applied with {@code MediaPlayer.setVolume(0.2f)} when + * using this class for playback.</li> + * + * <li>{@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} is also for a temporary request, + * but also expresses that your application expects the device to not play anything else. This is + * typically used if you are doing audio recording or speech recognition, and don’t want for + * examples notifications to be played by the system during that time.</li> + * </ul> + * + * <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests + * explained above. It is passed when building an {@code AudioFocusRequest} instance with its + * builder in the {@link Builder} constructor {@link Builder#Builder(int)}, or with + * {@link Builder#setFocusGain(int)} after copying an existing instance with + * {@link Builder#Builder(AudioFocusRequest)}. + * + * <h3>Qualifying your focus request</h3> + * <h4>Use case requiring a focus request</h4> + * <p>Any focus request is qualified by the {@link AudioAttributes} + * (see {@link Builder#setAudioAttributes(AudioAttributes)}) that describe the audio use case that + * will follow the request (once it's successful or granted). It is recommended to use the + * same {@code AudioAttributes} for the request as the attributes you are using for audio/media + * playback. + * <br>If no attributes are set, default attributes of {@link AudioAttributes#USAGE_MEDIA} are used. + * + * <h4>Delayed focus</h4> + * <p>Audio focus can be "locked" by the system for a number of reasons: during a phone call, when + * the car to which the device is connected plays an emergency message... To support these + * situations, the application can request to be notified when its request is fulfilled, by flagging + * its request as accepting delayed focus, with {@link Builder#setAcceptsDelayedFocusGain(boolean)}. + * <br>If focus is requested while being locked by the system, + * {@link AudioManager#requestAudioFocus(AudioFocusRequest)} will return + * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}. When focus isn't locked anymore, the focus + * listener set with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener)} + * or with {@link Builder#setOnAudioFocusChangeListener(OnAudioFocusChangeListener, Handler)} will + * be called to notify the application it now owns audio focus. + * + * <h4>Pausing vs ducking</h4> + * <p>When an application requested audio focus with + * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, the system will duck the current focus + * owner. Note that this behavior is <b>new for Android O<b>, whereas applications targeting SDK + * up to API 25, applications had to implement the ducking themselves when they received a focus + * loss of {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. + * <br>But ducking is not always the behavior expected by the user. A typical example is when the + * device plays driving directions while the user is listening to an audio book or podcast, and + * expects the audio playback to pause, instead of duck, as it is hard to understand a navigation + * prompt and spoken content at the same time. Therefore the system will not automatically duck + * when it detects it would be ducking spoken content: such content is detected when the + * {@code AudioAttributes} of the player are qualified by + * {@link AudioAttributes#CONTENT_TYPE_SPEECH}. Refer for instance to + * {@link AudioAttributes.Builder#setContentType(int)} and + * {@link MediaPlayer#setAudioAttributes(AudioAttributes)} if you are writing a media playback + * application for audio book, podcasts... Since the system will not automatically duck applications + * that play speech, it calls their focus listener instead to notify them of + * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, so they can pause instead. Note that + * this behavior is independent of the use of {@code AudioFocusRequest}, but tied to the use + * of {@code AudioAttributes}. + * <p>If your application requires pausing instead of ducking for any other reason than playing + * speech, you can also declare so with {@link Builder#setWillPauseWhenDucked(boolean)}, which will + * cause the system to call your focus listener instead of automatically ducking. + * */ -// TODO use this class to provide more documentation about audio focus and the new behaviors -// describe up to N, and after. public final class AudioFocusRequest { // default attributes for the request when not specified @@ -250,20 +340,38 @@ public final class AudioFocusRequest { * {@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; + } + + /** + * 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. If {@code null}, the {@code Handler} associated with the main - * {@link Looper} will be used. + * the notifications. * @return this {@code Builder} instance. - * @throws IllegalArgumentException thrown when a non-null handler is used with a null - * listener. + * @throws NullPointerException thrown when a null focus listener or handler is used. */ public @NonNull Builder setOnAudioFocusChangeListener( - @Nullable OnAudioFocusChangeListener listener, @Nullable Handler handler) { - if (listener == null && handler != null) { - throw new IllegalArgumentException( - "Illegal non-null handler without a focus listener"); + @NonNull OnAudioFocusChangeListener listener, @NonNull Handler handler) { + if (listener == null || handler == null) { + throw new NullPointerException("Illegal null focus listener or handler"); } mFocusListener = listener; mListenerHandler = handler; @@ -272,7 +380,7 @@ public final class AudioFocusRequest { /** * Sets the {@link AudioAttributes} to be associated with the focus request, and which - * describe the use case describing why focus is requested. + * 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 |