diff --git a/library/core/src/androidTest/java/com/google/android/exoplayer2/StreamVolumeManagerTest.java b/library/core/src/androidTest/java/com/google/android/exoplayer2/StreamVolumeManagerTest.java index 220f3313ec..792492dc4a 100644 --- a/library/core/src/androidTest/java/com/google/android/exoplayer2/StreamVolumeManagerTest.java +++ b/library/core/src/androidTest/java/com/google/android/exoplayer2/StreamVolumeManagerTest.java @@ -197,6 +197,30 @@ public class StreamVolumeManagerTest { }); } + @Test + public void setVolumeMuted_changesMuteState() { + testThread.runOnMainThread( + () -> { + int minVolume = streamVolumeManager.getMinVolume(); + int maxVolume = streamVolumeManager.getMaxVolume(); + if (minVolume == maxVolume || minVolume > 0) { + return; + } + + streamVolumeManager.setVolume(maxVolume); + assertThat(streamVolumeManager.isMuted()).isFalse(); + + streamVolumeManager.setMuted(true); + assertThat(streamVolumeManager.isMuted()).isTrue(); + assertThat(testListener.lastStreamVolumeMuted).isTrue(); + + streamVolumeManager.setMuted(false); + assertThat(streamVolumeManager.isMuted()).isFalse(); + assertThat(testListener.lastStreamVolumeMuted).isFalse(); + assertThat(testListener.lastStreamVolume).isEqualTo(maxVolume); + }); + } + @Test public void setStreamType_notifiesStreamTypeAndVolume() { testThread.runOnMainThread( @@ -250,6 +274,7 @@ public class StreamVolumeManagerTest { @C.StreamType private int lastStreamType; private int lastStreamVolume; + private boolean lastStreamVolumeMuted; public final CountDownLatch onStreamVolumeChangedLatch; public TestListener() { @@ -262,8 +287,9 @@ public class StreamVolumeManagerTest { } @Override - public void onStreamVolumeChanged(int streamVolume) { + public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { lastStreamVolume = streamVolume; + lastStreamVolumeMuted = streamMuted; onStreamVolumeChangedLatch.countDown(); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Player.java b/library/core/src/main/java/com/google/android/exoplayer2/Player.java index 518e331298..f692629dff 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Player.java @@ -397,6 +397,9 @@ public interface Player { */ int getDeviceVolume(); + /** Gets whether the device is muted or not. */ + boolean isDeviceMuted(); + /** * Sets the volume of the device. * @@ -409,6 +412,9 @@ public interface Player { /** Decreases the volume of the device. */ void decreaseDeviceVolume(); + + /** Sets the mute state of the device. */ + void setDeviceMuted(boolean muted); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 5d98e59e94..67c4b88899 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -1775,6 +1775,12 @@ public class SimpleExoPlayer extends BasePlayer return streamVolumeManager.getVolume(); } + @Override + public boolean isDeviceMuted() { + verifyApplicationThread(); + return streamVolumeManager.isMuted(); + } + @Override public void setDeviceVolume(int volume) { verifyApplicationThread(); @@ -1793,6 +1799,12 @@ public class SimpleExoPlayer extends BasePlayer streamVolumeManager.decreaseVolume(); } + @Override + public void setDeviceMuted(boolean muted) { + verifyApplicationThread(); + streamVolumeManager.setMuted(muted); + } + // Internal methods. private void removeSurfaceCallbacks() { @@ -2217,9 +2229,9 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public void onStreamVolumeChanged(int streamVolume) { + public void onStreamVolumeChanged(int streamVolume, boolean streamMuted) { for (DeviceListener deviceListener : deviceListeners) { - deviceListener.onDeviceVolumeChanged(streamVolume); + deviceListener.onDeviceVolumeChanged(streamVolume, streamMuted); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/StreamVolumeManager.java b/library/core/src/main/java/com/google/android/exoplayer2/StreamVolumeManager.java index 28f439d94f..59ab3f1616 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/StreamVolumeManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/StreamVolumeManager.java @@ -33,8 +33,8 @@ import com.google.android.exoplayer2.util.Util; /** Called when the audio stream type is changed. */ void onStreamTypeChanged(@C.StreamType int streamType); - /** Called when the audio stream volume is changed. */ - void onStreamVolumeChanged(int streamVolume); + /** 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. @@ -52,6 +52,7 @@ import com.google.android.exoplayer2.util.Util; @C.StreamType private int streamType; private int volume; + private boolean muted; /** Creates a manager. */ public StreamVolumeManager(Context context, Handler eventHandler, Listener listener) { @@ -63,7 +64,8 @@ import com.google.android.exoplayer2.util.Util; (AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE)); streamType = C.STREAM_TYPE_DEFAULT; - volume = audioManager.getStreamVolume(streamType); + volume = getVolumeFromManager(audioManager, streamType); + muted = getMutedFromManager(audioManager, streamType); receiver = new VolumeChangeReceiver(); IntentFilter filter = new IntentFilter(VOLUME_CHANGED_ACTION); @@ -102,6 +104,11 @@ import com.google.android.exoplayer2.util.Util; 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. @@ -138,16 +145,42 @@ import com.google.android.exoplayer2.util.Util; 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 = audioManager.getStreamVolume(streamType); - if (volume != newVolume) { + int newVolume = getVolumeFromManager(audioManager, streamType); + boolean newMuted = getMutedFromManager(audioManager, streamType); + if (volume != newVolume || muted != newMuted) { volume = newVolume; - listener.onStreamVolumeChanged(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; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/device/DeviceListener.java b/library/core/src/main/java/com/google/android/exoplayer2/device/DeviceListener.java index f310b6d553..3d35c6ad54 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/device/DeviceListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/device/DeviceListener.java @@ -23,6 +23,6 @@ public interface DeviceListener { /** Called when the device information changes. */ default void onDeviceInfoChanged(DeviceInfo deviceInfo) {} - /** Called when the device volume changes. */ - default void onDeviceVolumeChanged(int volume) {} + /** Called when the device volume or mute state changes. */ + default void onDeviceVolumeChanged(int volume, boolean muted) {} }