Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34a6663d authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "Audio focus API with AudioAttributes and more options"

parents 5b30c1bc 3db31acf
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -20660,6 +20660,26 @@ package android.media {
    field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
  }
  public final class AudioFocusRequest {
    method public boolean acceptsDelayedFocusGain();
    method public android.media.AudioAttributes getAudioAttributes();
    method public int getFocusGain();
    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
    method public android.os.Handler getOnAudioFocusChangeListenerHandler();
    method public boolean willPauseWhenDucked();
  }
  public static final class AudioFocusRequest.Builder {
    ctor public AudioFocusRequest.Builder(int);
    ctor public AudioFocusRequest.Builder(android.media.AudioFocusRequest);
    method public android.media.AudioFocusRequest build();
    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, android.os.Handler);
    method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean);
  }
  public final class AudioFormat implements android.os.Parcelable {
    method public int describeContents();
    method public int getChannelCount();
@@ -20736,6 +20756,7 @@ package android.media {
  public class AudioManager {
    method public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
    method public int abandonAudioFocusRequest(android.media.AudioFocusRequest);
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
@@ -20772,6 +20793,7 @@ package android.media {
    method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
    method public deprecated boolean registerRemoteController(android.media.RemoteController);
    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
    method public int requestAudioFocus(android.media.AudioFocusRequest);
    method public deprecated void setBluetoothA2dpOn(boolean);
    method public void setBluetoothScoOn(boolean);
    method public void setMicrophoneMute(boolean);
@@ -20815,6 +20837,7 @@ package android.media {
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_DELAYED = 2; // 0x2
    field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
    field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
+23 −0
Original line number Diff line number Diff line
@@ -22318,6 +22318,26 @@ package android.media {
    field public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR;
  }
  public final class AudioFocusRequest {
    method public boolean acceptsDelayedFocusGain();
    method public android.media.AudioAttributes getAudioAttributes();
    method public int getFocusGain();
    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
    method public android.os.Handler getOnAudioFocusChangeListenerHandler();
    method public boolean willPauseWhenDucked();
  }
  public static final class AudioFocusRequest.Builder {
    ctor public AudioFocusRequest.Builder(int);
    ctor public AudioFocusRequest.Builder(android.media.AudioFocusRequest);
    method public android.media.AudioFocusRequest build();
    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, android.os.Handler);
    method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean);
  }
  public final class AudioFormat implements android.os.Parcelable {
    method public int describeContents();
    method public int getChannelCount();
@@ -22395,6 +22415,7 @@ package android.media {
  public class AudioManager {
    method public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
    method public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
    method public int abandonAudioFocusRequest(android.media.AudioFocusRequest);
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
@@ -22433,6 +22454,7 @@ package android.media {
    method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
    method public deprecated boolean registerRemoteController(android.media.RemoteController);
    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
    method public int requestAudioFocus(android.media.AudioFocusRequest);
    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
    method public deprecated void setBluetoothA2dpOn(boolean);
@@ -22482,6 +22504,7 @@ package android.media {
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_DELAYED = 2; // 0x2
    field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
    field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
+23 −0
Original line number Diff line number Diff line
@@ -20756,6 +20756,26 @@ package android.media {
    field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
  }
  public final class AudioFocusRequest {
    method public boolean acceptsDelayedFocusGain();
    method public android.media.AudioAttributes getAudioAttributes();
    method public int getFocusGain();
    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
    method public android.os.Handler getOnAudioFocusChangeListenerHandler();
    method public boolean willPauseWhenDucked();
  }
  public static final class AudioFocusRequest.Builder {
    ctor public AudioFocusRequest.Builder(int);
    ctor public AudioFocusRequest.Builder(android.media.AudioFocusRequest);
    method public android.media.AudioFocusRequest build();
    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, android.os.Handler);
    method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean);
  }
  public final class AudioFormat implements android.os.Parcelable {
    method public int describeContents();
    method public int getChannelCount();
@@ -20832,6 +20852,7 @@ package android.media {
  public class AudioManager {
    method public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener);
    method public int abandonAudioFocusRequest(android.media.AudioFocusRequest);
    method public void adjustStreamVolume(int, int, int);
    method public void adjustSuggestedStreamVolume(int, int, int);
    method public void adjustVolume(int, int);
@@ -20868,6 +20889,7 @@ package android.media {
    method public deprecated void registerRemoteControlClient(android.media.RemoteControlClient);
    method public deprecated boolean registerRemoteController(android.media.RemoteController);
    method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
    method public int requestAudioFocus(android.media.AudioFocusRequest);
    method public deprecated void setBluetoothA2dpOn(boolean);
    method public void setBluetoothScoOn(boolean);
    method public void setMicrophoneMute(boolean);
@@ -20911,6 +20933,7 @@ package android.media {
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; // 0xfffffffe
    field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
    field public static final int AUDIOFOCUS_NONE = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_DELAYED = 2; // 0x2
    field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
    field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
    field public static final int AUDIO_SESSION_ID_GENERATE = 0; // 0x0
+321 −0
Original line number Diff line number Diff line
/*
 * 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.media.AudioManager.OnAudioFocusChangeListener;
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)}.
 * <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.
 */
// 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
    private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA).build();

    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;

    //TODO implement use of optional handler
    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;
        }
    }

    /**
     * Returns the focus change listener set for this {@code AudioFocusRequest}.
     * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
     */
    public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
        return mFocusListener;
    }

    /**
     * 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;
    }

    int getFlags() {
        return mFlags;
    }

    /**
     * Builder class for {@link AudioFocusRequest} objects.
     * <p> Here is an example where {@code Builder} is used to define the
     * {@link AudioFocusRequest} for requesting audio focus:
     *
     * <pre class="prettyprint">
     * mAudioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
     * mPlaybackAttributes = new AudioAttributes.Builder()
     *         .setUsage(AudioAttributes.USAGE_GAME)
     *         .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
     *         .build();
     * mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
     *         .setAudioAttributes(mPlaybackAttributes)
     *         .setAcceptsDelayedFocusGain(true)
     *         .setOnAudioFocusChangeListener(mMyFocusListener, mMyHandler)
     *         .build();
     * mMediaPlayer = new MediaPlayer();
     *  ...
     * mMediaPlayer.setAudioAttributes(mPlaybackAttributes);
     *  ...
     * mAudioManager.requestAudioFocus(mFocusRequest);
     *  ...
     * mAudioManager.abandonAudioFocusRequest(mFocusRequest);
     * </pre>
     *
     */
    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;

        /**
         * 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, 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.
         * @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.
         * @return this {@code Builder} instance.
         * @throws IllegalArgumentException thrown when a non-null handler is used with a null
         *   listener.
         */
        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");
            }
            mFocusListener = listener;
            mListenerHandler = handler;
            return this;
        }

        /**
         * Sets the {@link AudioAttributes} to be associated with the focus request, and which
         * describe the use case describing why 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 IllegalArgumentException thrown when using null for the attributes.
         */
        public @NonNull Builder setAudioAttributes(@NonNull AudioAttributes attributes) {
            if (attributes == null) {
                throw new IllegalArgumentException("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;
        }

        /**
         * 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 IllegalArgumentException thrown when focus request 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 IllegalArgumentException(
                        "Can't use delayed focus or pause on duck without a listener");
            }
            final int flags = 0
                    | (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0)
                    | (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0);
            return new AudioFocusRequest(mFocusListener, mListenerHandler,
                    mAttr, mFocusGain, flags);
        }
    }
}
+41 −4

File changed.

Preview size limit exceeded, changes collapsed.