400 lines
14 KiB
Java
400 lines
14 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2022 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.content.Context;
|
||
|
import android.media.audiopolicy.AudioVolumeGroup;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.os.RemoteException;
|
||
|
import android.os.ServiceManager;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
* A class to represent volume information.
|
||
|
* Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
|
||
|
* Volume index is optional when used to represent a category of volume.
|
||
|
* Volume ranges are supported too, making the representation of volume changes agnostic regarding
|
||
|
* the range of values that are supported (e.g. can be used to map BT A2DP absolute volume range to
|
||
|
* internal range).
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public final class VolumeInfo implements Parcelable {
|
||
|
private static final String TAG = "VolumeInfo";
|
||
|
|
||
|
private final boolean mUsesStreamType; // false implies AudioVolumeGroup is used
|
||
|
private final boolean mHasMuteCommand;
|
||
|
private final boolean mIsMuted;
|
||
|
private final int mVolIndex;
|
||
|
private final int mMinVolIndex;
|
||
|
private final int mMaxVolIndex;
|
||
|
private final @Nullable AudioVolumeGroup mVolGroup;
|
||
|
private final @AudioManager.PublicStreamTypes int mStreamType;
|
||
|
|
||
|
private static IAudioService sService;
|
||
|
private static VolumeInfo sDefaultVolumeInfo;
|
||
|
|
||
|
private VolumeInfo(boolean usesStreamType, boolean hasMuteCommand, boolean isMuted,
|
||
|
int volIndex, int minVolIndex, int maxVolIndex,
|
||
|
AudioVolumeGroup volGroup, int streamType) {
|
||
|
mUsesStreamType = usesStreamType;
|
||
|
mHasMuteCommand = hasMuteCommand;
|
||
|
mIsMuted = isMuted;
|
||
|
mVolIndex = volIndex;
|
||
|
mMinVolIndex = minVolIndex;
|
||
|
mMaxVolIndex = maxVolIndex;
|
||
|
mVolGroup = volGroup;
|
||
|
mStreamType = streamType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Indicates whether this instance has a stream type associated to it.
|
||
|
* Note this method returning true implies {@link #hasVolumeGroup()} returns false.
|
||
|
* (e.g. {@link AudioManager#STREAM_MUSIC}).
|
||
|
* @return true if it has stream type information
|
||
|
*/
|
||
|
public boolean hasStreamType() {
|
||
|
return mUsesStreamType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the associated stream type, or will throw if {@link #hasStreamType()} returned false.
|
||
|
* @return a stream type value, see AudioManager.STREAM_*
|
||
|
* @throws IllegalStateException when called on a VolumeInfo not configured for
|
||
|
* stream types.
|
||
|
*/
|
||
|
public @AudioManager.PublicStreamTypes int getStreamType() {
|
||
|
if (!mUsesStreamType) {
|
||
|
throw new IllegalStateException("VolumeInfo doesn't use stream types");
|
||
|
}
|
||
|
return mStreamType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Indicates whether this instance has a {@link AudioVolumeGroup} associated to it.
|
||
|
* Note this method returning true implies {@link #hasStreamType()} returns false.
|
||
|
* @return true if it has volume group information
|
||
|
*/
|
||
|
public boolean hasVolumeGroup() {
|
||
|
return !mUsesStreamType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the associated volume group, or will throw if {@link #hasVolumeGroup()} returned
|
||
|
* false.
|
||
|
* @return the volume group corresponding to this VolumeInfo
|
||
|
* @throws IllegalStateException when called on a VolumeInfo not configured for
|
||
|
* volume groups.
|
||
|
*/
|
||
|
public @NonNull AudioVolumeGroup getVolumeGroup() {
|
||
|
if (mUsesStreamType) {
|
||
|
throw new IllegalStateException("VolumeInfo doesn't use AudioVolumeGroup");
|
||
|
}
|
||
|
return mVolGroup;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return whether this instance is conveying a mute state
|
||
|
* @return true if the muted state was explicitly set for this instance
|
||
|
*/
|
||
|
public boolean hasMuteCommand() {
|
||
|
return mHasMuteCommand;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether this instance is conveying a mute state that was explicitly set
|
||
|
* by {@link Builder#setMuted(boolean)}, false otherwise
|
||
|
* @return true if the volume state is muted
|
||
|
*/
|
||
|
public boolean isMuted() {
|
||
|
return mIsMuted;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A value used to express no volume index has been set.
|
||
|
*/
|
||
|
public static final int INDEX_NOT_SET = -100;
|
||
|
|
||
|
/**
|
||
|
* Returns the volume index.
|
||
|
* @return a volume index, or {@link #INDEX_NOT_SET} if no index was set, in which case this
|
||
|
* instance is used to express a volume representation type (stream vs group) and
|
||
|
* optionally its volume range
|
||
|
*/
|
||
|
public int getVolumeIndex() {
|
||
|
return mVolIndex;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the minimum volume index.
|
||
|
* @return the minimum volume index, or {@link #INDEX_NOT_SET} if no minimum index was set.
|
||
|
*/
|
||
|
public int getMinVolumeIndex() {
|
||
|
return mMinVolIndex;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the maximum volume index.
|
||
|
* @return the maximum volume index, or {@link #INDEX_NOT_SET} if no maximum index was
|
||
|
* set.
|
||
|
*/
|
||
|
public int getMaxVolumeIndex() {
|
||
|
return mMaxVolIndex;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the default info for the platform, typically initialized
|
||
|
* to STREAM_MUSIC with min/max initialized to the associated range
|
||
|
* @return the default VolumeInfo for the device
|
||
|
*/
|
||
|
public static @NonNull VolumeInfo getDefaultVolumeInfo() {
|
||
|
if (sService == null) {
|
||
|
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
|
||
|
sService = IAudioService.Stub.asInterface(b);
|
||
|
}
|
||
|
if (sDefaultVolumeInfo == null) {
|
||
|
try {
|
||
|
sDefaultVolumeInfo = sService.getDefaultVolumeInfo();
|
||
|
} catch (RemoteException e) {
|
||
|
Log.e(TAG, "Error calling getDefaultVolumeInfo", e);
|
||
|
// return a valid value, but don't cache it
|
||
|
return new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build();
|
||
|
}
|
||
|
}
|
||
|
return sDefaultVolumeInfo;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The builder class for creating and initializing, or copying and modifying VolumeInfo
|
||
|
* instances
|
||
|
*/
|
||
|
public static final class Builder {
|
||
|
private boolean mUsesStreamType = true; // false implies AudioVolumeGroup is used
|
||
|
private @AudioManager.PublicStreamTypes int mStreamType = AudioManager.STREAM_MUSIC;
|
||
|
private boolean mHasMuteCommand = false;
|
||
|
private boolean mIsMuted = false;
|
||
|
private int mVolIndex = INDEX_NOT_SET;
|
||
|
private int mMinVolIndex = INDEX_NOT_SET;
|
||
|
private int mMaxVolIndex = INDEX_NOT_SET;
|
||
|
private @Nullable AudioVolumeGroup mVolGroup;
|
||
|
|
||
|
/**
|
||
|
* Builder constructor for stream type-based VolumeInfo
|
||
|
*/
|
||
|
public Builder(@AudioManager.PublicStreamTypes int streamType) {
|
||
|
if (!AudioManager.isPublicStreamType(streamType)) {
|
||
|
throw new IllegalArgumentException("Not a valid public stream type " + streamType);
|
||
|
}
|
||
|
mUsesStreamType = true;
|
||
|
mStreamType = streamType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Builder constructor for volume group-based VolumeInfo
|
||
|
*/
|
||
|
public Builder(@NonNull AudioVolumeGroup volGroup) {
|
||
|
Objects.requireNonNull(volGroup);
|
||
|
mUsesStreamType = false;
|
||
|
mStreamType = -Integer.MIN_VALUE;
|
||
|
mVolGroup = volGroup;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Builder constructor to copy a given VolumeInfo.
|
||
|
* Note you can't change the stream type or volume group later.
|
||
|
*/
|
||
|
public Builder(@NonNull VolumeInfo info) {
|
||
|
Objects.requireNonNull(info);
|
||
|
mUsesStreamType = info.mUsesStreamType;
|
||
|
mStreamType = info.mStreamType;
|
||
|
mHasMuteCommand = info.mHasMuteCommand;
|
||
|
mIsMuted = info.mIsMuted;
|
||
|
mVolIndex = info.mVolIndex;
|
||
|
mMinVolIndex = info.mMinVolIndex;
|
||
|
mMaxVolIndex = info.mMaxVolIndex;
|
||
|
mVolGroup = info.mVolGroup;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets whether the volume is in a muted state
|
||
|
* @param isMuted
|
||
|
* @return the same builder instance
|
||
|
*/
|
||
|
public @NonNull Builder setMuted(boolean isMuted) {
|
||
|
mHasMuteCommand = true;
|
||
|
mIsMuted = isMuted;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the volume index
|
||
|
* @param volIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
|
||
|
* @return the same builder instance
|
||
|
*/
|
||
|
public @NonNull Builder setVolumeIndex(int volIndex) {
|
||
|
if (volIndex != INDEX_NOT_SET && volIndex < 0) {
|
||
|
throw new IllegalArgumentException("Volume index cannot be negative");
|
||
|
}
|
||
|
mVolIndex = volIndex;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the minimum volume index
|
||
|
* @param minIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
|
||
|
* @return the same builder instance
|
||
|
*/
|
||
|
public @NonNull Builder setMinVolumeIndex(int minIndex) {
|
||
|
if (minIndex != INDEX_NOT_SET && minIndex < 0) {
|
||
|
throw new IllegalArgumentException("Min volume index cannot be negative");
|
||
|
}
|
||
|
mMinVolIndex = minIndex;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the maximum volume index
|
||
|
* @param maxIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
|
||
|
* @return the same builder instance
|
||
|
*/
|
||
|
public @NonNull Builder setMaxVolumeIndex(int maxIndex) {
|
||
|
if (maxIndex != INDEX_NOT_SET && maxIndex < 0) {
|
||
|
throw new IllegalArgumentException("Max volume index cannot be negative");
|
||
|
}
|
||
|
mMaxVolIndex = maxIndex;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Builds the VolumeInfo with the data given to the builder
|
||
|
* @return the new VolumeInfo instance
|
||
|
*/
|
||
|
public @NonNull VolumeInfo build() {
|
||
|
if (mVolIndex != INDEX_NOT_SET) {
|
||
|
if (mMinVolIndex != INDEX_NOT_SET && mVolIndex < mMinVolIndex) {
|
||
|
throw new IllegalArgumentException("Volume index:" + mVolIndex
|
||
|
+ " lower than min index:" + mMinVolIndex);
|
||
|
}
|
||
|
if (mMaxVolIndex != INDEX_NOT_SET && mVolIndex > mMaxVolIndex) {
|
||
|
throw new IllegalArgumentException("Volume index:" + mVolIndex
|
||
|
+ " greater than max index:" + mMaxVolIndex);
|
||
|
}
|
||
|
}
|
||
|
if (mMinVolIndex != INDEX_NOT_SET && mMaxVolIndex != INDEX_NOT_SET
|
||
|
&& mMinVolIndex > mMaxVolIndex) {
|
||
|
throw new IllegalArgumentException("Min volume index:" + mMinVolIndex
|
||
|
+ " greater than max index:" + mMaxVolIndex);
|
||
|
}
|
||
|
return new VolumeInfo(mUsesStreamType, mHasMuteCommand, mIsMuted,
|
||
|
mVolIndex, mMinVolIndex, mMaxVolIndex,
|
||
|
mVolGroup, mStreamType);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------
|
||
|
// Parcelable
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(mUsesStreamType, mHasMuteCommand, mStreamType, mIsMuted,
|
||
|
mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroup);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(Object o) {
|
||
|
if (this == o) return true;
|
||
|
if (o == null || getClass() != o.getClass()) return false;
|
||
|
|
||
|
VolumeInfo that = (VolumeInfo) o;
|
||
|
return ((mUsesStreamType == that.mUsesStreamType)
|
||
|
&& (mStreamType == that.mStreamType)
|
||
|
&& (mHasMuteCommand == that.mHasMuteCommand)
|
||
|
&& (mIsMuted == that.mIsMuted)
|
||
|
&& (mVolIndex == that.mVolIndex)
|
||
|
&& (mMinVolIndex == that.mMinVolIndex)
|
||
|
&& (mMaxVolIndex == that.mMaxVolIndex)
|
||
|
&& Objects.equals(mVolGroup, that.mVolGroup));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return new String("VolumeInfo:"
|
||
|
+ (mUsesStreamType ? (" streamType:" + mStreamType)
|
||
|
: (" volGroup:" + mVolGroup))
|
||
|
+ (mHasMuteCommand ? (" muted:" + mIsMuted) : ("[no mute cmd]"))
|
||
|
+ ((mVolIndex != INDEX_NOT_SET) ? (" volIndex:" + mVolIndex) : "")
|
||
|
+ ((mMinVolIndex != INDEX_NOT_SET) ? (" min:" + mMinVolIndex) : "")
|
||
|
+ ((mMaxVolIndex != INDEX_NOT_SET) ? (" max:" + mMaxVolIndex) : ""));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||
|
dest.writeBoolean(mUsesStreamType);
|
||
|
dest.writeInt(mStreamType);
|
||
|
dest.writeBoolean(mHasMuteCommand);
|
||
|
dest.writeBoolean(mIsMuted);
|
||
|
dest.writeInt(mVolIndex);
|
||
|
dest.writeInt(mMinVolIndex);
|
||
|
dest.writeInt(mMaxVolIndex);
|
||
|
if (!mUsesStreamType) {
|
||
|
mVolGroup.writeToParcel(dest, 0 /*ignored*/);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private VolumeInfo(@NonNull Parcel in) {
|
||
|
mUsesStreamType = in.readBoolean();
|
||
|
mStreamType = in.readInt();
|
||
|
mHasMuteCommand = in.readBoolean();
|
||
|
mIsMuted = in.readBoolean();
|
||
|
mVolIndex = in.readInt();
|
||
|
mMinVolIndex = in.readInt();
|
||
|
mMaxVolIndex = in.readInt();
|
||
|
if (!mUsesStreamType) {
|
||
|
mVolGroup = AudioVolumeGroup.CREATOR.createFromParcel(in);
|
||
|
} else {
|
||
|
mVolGroup = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static final @NonNull Parcelable.Creator<VolumeInfo> CREATOR =
|
||
|
new Parcelable.Creator<VolumeInfo>() {
|
||
|
/**
|
||
|
* Rebuilds a VolumeInfo previously stored with writeToParcel().
|
||
|
* @param p Parcel object to read the VolumeInfo from
|
||
|
* @return a new VolumeInfo created from the data in the parcel
|
||
|
*/
|
||
|
public VolumeInfo createFromParcel(Parcel p) {
|
||
|
return new VolumeInfo(p);
|
||
|
}
|
||
|
|
||
|
public VolumeInfo[] newArray(int size) {
|
||
|
return new VolumeInfo[size];
|
||
|
}
|
||
|
};
|
||
|
}
|