blob: 59ab3f161661f5a6f8b9373e790e8c1edcd29dcf [file] [log] [blame]
/*
* Copyright 2020 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 com.google.android.exoplayer2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Handler;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/** A manager that wraps {@link AudioManager} to control/listen audio stream volume. */
/* package */ final class StreamVolumeManager {
/** A listener for changes in the manager. */
public interface Listener {
/** Called when the audio stream type is changed. */
void onStreamTypeChanged(@C.StreamType int streamType);
/** Called when the audio stream volume or mute state is changed. */
void onStreamVolumeChanged(int streamVolume, boolean streamMuted);
}
// TODO(b/151280453): Replace the hidden intent action with an official one.
// Copied from AudioManager#VOLUME_CHANGED_ACTION
private static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
// TODO(b/153317944): Allow users to override these flags.
private static final int VOLUME_FLAGS = AudioManager.FLAG_SHOW_UI;
private final Context applicationContext;
private final Handler eventHandler;
private final Listener listener;
private final AudioManager audioManager;
private final VolumeChangeReceiver receiver;
@C.StreamType private int streamType;
private int volume;
private boolean muted;
/** Creates a manager. */
public StreamVolumeManager(Context context, Handler eventHandler, Listener listener) {
applicationContext = context.getApplicationContext();
this.eventHandler = eventHandler;
this.listener = listener;
audioManager =
Assertions.checkStateNotNull(
(AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE));
streamType = C.STREAM_TYPE_DEFAULT;
volume = getVolumeFromManager(audioManager, streamType);
muted = getMutedFromManager(audioManager, streamType);
receiver = new VolumeChangeReceiver();
IntentFilter filter = new IntentFilter(VOLUME_CHANGED_ACTION);
applicationContext.registerReceiver(receiver, filter);
}
/** Sets the audio stream type. */
public void setStreamType(@C.StreamType int streamType) {
if (this.streamType == streamType) {
return;
}
this.streamType = streamType;
updateVolumeAndNotifyIfChanged();
listener.onStreamTypeChanged(streamType);
}
/**
* Gets the minimum volume for the current audio stream. It can be changed if {@link
* #setStreamType(int)} is called.
*/
public int getMinVolume() {
return Util.SDK_INT >= 28 ? audioManager.getStreamMinVolume(streamType) : 0;
}
/**
* Gets the maximum volume for the current audio stream. It can be changed if {@link
* #setStreamType(int)} is called.
*/
public int getMaxVolume() {
return audioManager.getStreamMaxVolume(streamType);
}
/** Gets the current volume for the current audio stream. */
public int getVolume() {
return volume;
}
/** Gets whether the current audio stream is muted or not. */
public boolean isMuted() {
return muted;
}
/**
* Sets the volume with the given value for the current audio stream. The value should be between
* {@link #getMinVolume()} and {@link #getMaxVolume()}, otherwise it will be ignored.
*/
public void setVolume(int volume) {
if (volume < getMinVolume() || volume > getMaxVolume()) {
return;
}
audioManager.setStreamVolume(streamType, volume, VOLUME_FLAGS);
updateVolumeAndNotifyIfChanged();
}
/**
* Increases the volume by one for the current audio stream. It will be ignored if the current
* volume is equal to {@link #getMaxVolume()}.
*/
public void increaseVolume() {
if (volume >= getMaxVolume()) {
return;
}
audioManager.adjustStreamVolume(streamType, AudioManager.ADJUST_RAISE, VOLUME_FLAGS);
updateVolumeAndNotifyIfChanged();
}
/**
* Decreases the volume by one for the current audio stream. It will be ignored if the current
* volume is equal to {@link #getMinVolume()}.
*/
public void decreaseVolume() {
if (volume <= getMinVolume()) {
return;
}
audioManager.adjustStreamVolume(streamType, AudioManager.ADJUST_LOWER, VOLUME_FLAGS);
updateVolumeAndNotifyIfChanged();
}
/** Sets the mute state of the current audio stream. */
public void setMuted(boolean muted) {
if (Util.SDK_INT >= 23) {
audioManager.adjustStreamVolume(
streamType, muted ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, VOLUME_FLAGS);
} else {
audioManager.setStreamMute(streamType, muted);
}
updateVolumeAndNotifyIfChanged();
}
/** Releases the manager. It must be called when the manager is no longer required. */
public void release() {
applicationContext.unregisterReceiver(receiver);
}
private void updateVolumeAndNotifyIfChanged() {
int newVolume = getVolumeFromManager(audioManager, streamType);
boolean newMuted = getMutedFromManager(audioManager, streamType);
if (volume != newVolume || muted != newMuted) {
volume = newVolume;
muted = newMuted;
listener.onStreamVolumeChanged(newVolume, newMuted);
}
}
private static int getVolumeFromManager(AudioManager audioManager, @C.StreamType int streamType) {
return audioManager.getStreamVolume(streamType);
}
private static boolean getMutedFromManager(
AudioManager audioManager, @C.StreamType int streamType) {
if (Util.SDK_INT >= 23) {
return audioManager.isStreamMute(streamType);
} else {
return audioManager.getStreamVolume(streamType) == 0;
}
}
private final class VolumeChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
eventHandler.post(StreamVolumeManager.this::updateVolumeAndNotifyIfChanged);
}
}
}