diff --git a/RELEASENOTES.md b/RELEASENOTES.md index efb1ef0ef0..93bdd43a99 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,9 +47,17 @@ objects that are dispatched by the dispatcher. * Rename `ExoTrackSelection.blacklist` to `excludeTrack` and `isBlacklisted` to `isTrackExcluded`. - * Deprecate `Player.COMMAND_GET_MEDIA_ITEMS_METADATA` and - `COMMAND_SET_MEDIA_ITEMS_METADATA`. Use `COMMAND_GET_METADATA` and - `COMMAND_SET_PLAYLIST_METADATA` instead. + * Add commands to Player: + * `COMMAND_GET_METADATA` + * `COMMAND_SET_PLAYLIST_METADATA` + * `COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS` + * `COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS` + * Add overloaded methods to Player which allow users to specify volume + flags: + * `void setDeviceVolume(int, int)` + * `void increaseDeviceVolume(int)` + * `void decreaseDeviceVolume(int)` + * `void setDeviceMuted(boolean, int)` * Add `Buffer.isLastSample()` that denotes if `Buffer` contains flag `C.BUFFER_FLAG_LAST_SAMPLE`. * Fix issue where last frame may not be rendered if the last sample with @@ -64,13 +72,16 @@ * Fix parsing of H.265 SPS in MPEG-TS files by re-using the parsing logic already used by RTSP and MP4 extractors ([#303](https://github.com/androidx/media/issues/303)). +* ExoPlayer: + * Allow ExoPlayer to have control of device volume methods only if + explicitly opted in. Use + `ExoPlayer.Builder.setDeviceVolumeControlEnabled` to have access to: + * `getDeviceVolume()` + * `isDeviceMuted()` + * `setDeviceVolume(int)` and `setDeviceVolume(int, int)` + * `increaseDeviceVolume(int)` and `increaseDeviceVolume(int, int)` + * `decreaseDeviceVolume(int)` and `decreaseDeviceVolume(int, int)` * Session: - * Deprecate 4 volume-controlling methods in `Player` and add overloaded - methods which allow users to specify volume flags: - * `void setDeviceVolume(int, int)` - * `void increaseDeviceVolume(int)` - * `void decreaseDeviceVolume(int)` - * `void setDeviceMuted(boolean, int)` * Fix issue where `MediaController` doesn't update its available commands when connected to a legacy `MediaSessionCompat` that updates its actions. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index bb40ee654a..7bc70b0f1b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -479,6 +479,7 @@ public interface ExoPlayer extends Player { @C.WakeMode /* package */ int wakeMode; /* package */ boolean handleAudioBecomingNoisy; /* package */ boolean skipSilenceEnabled; + /* package */ boolean deviceVolumeControlEnabled; @C.VideoScalingMode /* package */ int videoScalingMode; @C.VideoChangeFrameRateStrategy /* package */ int videoChangeFrameRateStrategy; /* package */ boolean useLazyPreparation; @@ -918,6 +919,21 @@ public interface ExoPlayer extends Player { return this; } + /** + * Sets whether the player is allowed to set, increase, decrease or mute device volume. + * + * @param deviceVolumeControlEnabled Whether controlling device volume is enabled. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + @CanIgnoreReturnValue + @UnstableApi + public Builder setDeviceVolumeControlEnabled(boolean deviceVolumeControlEnabled) { + checkState(!buildCalled); + this.deviceVolumeControlEnabled = deviceVolumeControlEnabled; + return this; + } + /** * Sets the {@link C.VideoScalingMode} that will be used by the player. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 2bdbb02e57..e063f6c53b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -168,7 +168,7 @@ import java.util.concurrent.TimeoutException; private final FrameMetadataListener frameMetadataListener; private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; - private final StreamVolumeManager streamVolumeManager; + @Nullable private final StreamVolumeManager streamVolumeManager; private final WakeLockManager wakeLockManager; private final WifiLockManager wifiLockManager; private final long detachSurfaceTimeoutMs; @@ -228,6 +228,7 @@ import java.util.concurrent.TimeoutException; private long maskingWindowPositionMs; @SuppressLint("HandlerLeak") + @SuppressWarnings("deprecation") // Control flow for old volume commands public ExoPlayerImpl(ExoPlayer.Builder builder, @Nullable Player wrappingPlayer) { constructorFinished = new ConditionVariable(); try { @@ -306,17 +307,17 @@ import java.util.concurrent.TimeoutException; COMMAND_GET_TRACKS, COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, COMMAND_SET_VIDEO_SURFACE, COMMAND_GET_TEXT, COMMAND_RELEASE) .addIf( COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) + .addIf(COMMAND_GET_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_SET_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_ADJUST_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, builder.deviceVolumeControlEnabled) .build(); availableCommands = new Commands.Builder() @@ -381,9 +382,13 @@ import java.util.concurrent.TimeoutException; audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); - streamVolumeManager = - new StreamVolumeManager(builder.context, eventHandler, componentListener); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + if (builder.deviceVolumeControlEnabled) { + streamVolumeManager = + new StreamVolumeManager(builder.context, eventHandler, componentListener); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + } else { + streamVolumeManager = null; + } wakeLockManager = new WakeLockManager(builder.context); wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); wifiLockManager = new WifiLockManager(builder.context); @@ -999,7 +1004,9 @@ import java.util.concurrent.TimeoutException; keepSessionIdAudioTrack = null; } audioBecomingNoisyManager.setEnabled(false); - streamVolumeManager.release(); + if (streamVolumeManager != null) { + streamVolumeManager.release(); + } wakeLockManager.setStayAwake(false); wifiLockManager.setStayAwake(false); audioFocusManager.release(); @@ -1421,7 +1428,10 @@ import java.util.concurrent.TimeoutException; if (!Util.areEqual(this.audioAttributes, newAudioAttributes)) { this.audioAttributes = newAudioAttributes; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, newAudioAttributes); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(newAudioAttributes.usage)); + if (streamVolumeManager != null) { + streamVolumeManager.setStreamType( + Util.getStreamTypeForAudioUsage(newAudioAttributes.usage)); + } // Queue event only and flush after updating playWhenReady in case both events are triggered. listeners.queueEvent( EVENT_AUDIO_ATTRIBUTES_CHANGED, @@ -1703,13 +1713,21 @@ import java.util.concurrent.TimeoutException; @Override public int getDeviceVolume() { verifyApplicationThread(); - return streamVolumeManager.getVolume(); + if (streamVolumeManager != null) { + return streamVolumeManager.getVolume(); + } else { + return 0; + } } @Override public boolean isDeviceMuted() { verifyApplicationThread(); - return streamVolumeManager.isMuted(); + if (streamVolumeManager != null) { + return streamVolumeManager.isMuted(); + } else { + return false; + } } /** @@ -1719,13 +1737,17 @@ import java.util.concurrent.TimeoutException; @Override public void setDeviceVolume(int volume) { verifyApplicationThread(); - streamVolumeManager.setVolume(volume, C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.setVolume(volume, C.VOLUME_FLAG_SHOW_UI); + } } @Override public void setDeviceVolume(int volume, @C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.setVolume(volume, flags); + if (streamVolumeManager != null) { + streamVolumeManager.setVolume(volume, flags); + } } /** @@ -1735,13 +1757,17 @@ import java.util.concurrent.TimeoutException; @Override public void increaseDeviceVolume() { verifyApplicationThread(); - streamVolumeManager.increaseVolume(C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.increaseVolume(C.VOLUME_FLAG_SHOW_UI); + } } @Override public void increaseDeviceVolume(@C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.increaseVolume(flags); + if (streamVolumeManager != null) { + streamVolumeManager.increaseVolume(flags); + } } /** @@ -1751,13 +1777,17 @@ import java.util.concurrent.TimeoutException; @Override public void decreaseDeviceVolume() { verifyApplicationThread(); - streamVolumeManager.decreaseVolume(C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.decreaseVolume(C.VOLUME_FLAG_SHOW_UI); + } } @Override public void decreaseDeviceVolume(@C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.decreaseVolume(flags); + if (streamVolumeManager != null) { + streamVolumeManager.decreaseVolume(flags); + } } /** @@ -1767,13 +1797,17 @@ import java.util.concurrent.TimeoutException; @Override public void setDeviceMuted(boolean muted) { verifyApplicationThread(); - streamVolumeManager.setMuted(muted, C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.setMuted(muted, C.VOLUME_FLAG_SHOW_UI); + } } @Override public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.setMuted(muted, flags); + if (streamVolumeManager != null) { + streamVolumeManager.setMuted(muted, flags); + } } @Override @@ -2797,10 +2831,10 @@ import java.util.concurrent.TimeoutException; } } - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { + private static DeviceInfo createDeviceInfo(@Nullable StreamVolumeManager streamVolumeManager) { return new DeviceInfo.Builder(DeviceInfo.PLAYBACK_TYPE_LOCAL) - .setMinVolume(streamVolumeManager.getMinVolume()) - .setMaxVolume(streamVolumeManager.getMaxVolume()) + .setMinVolume(streamVolumeManager != null ? streamVolumeManager.getMinVolume() : 0) + .setMaxVolume(streamVolumeManager != null ? streamVolumeManager.getMaxVolume() : 0) .build(); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index c2a093a1be..dd74cd6b9e 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -100,6 +100,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; @@ -9155,6 +9156,7 @@ public final class ExoPlayerTest { player.release(); } + @SuppressWarnings("deprecation") // Checking old volume commands @Test public void isCommandAvailable_isTrueForAvailableCommands() { ExoPlayer player = new TestExoPlayerBuilder(context).build(); @@ -9184,10 +9186,7 @@ public final class ExoPlayerTest { assertThat(player.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)).isTrue(); @@ -9195,6 +9194,33 @@ public final class ExoPlayerTest { assertThat(player.isCommandAvailable(COMMAND_RELEASE)).isTrue(); } + @SuppressWarnings("deprecation") // Checking old volume commands + @Test + public void isCommandAvailable_withDeviceVolumeControlEnabled_isTrueForDeviceVolumeCommands() { + ExoPlayer player = + new TestExoPlayerBuilder(context).setDeviceVolumeControlEnabled(true).build(); + + assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS)).isTrue(); + } + + @SuppressWarnings("deprecation") // Checking old volume commands + @Test + public void + isCommandAvailable_withoutDeviceVolumeControlEnabled_isFalseForDeviceVolumeCommands() { + ExoPlayer player = + new TestExoPlayerBuilder(context).setDeviceVolumeControlEnabled(false).build(); + + assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS)).isFalse(); + } + @Test public void isCommandAvailable_duringAd_isFalseForSeekCommands() throws Exception { AdPlaybackState adPlaybackState = @@ -9529,7 +9555,9 @@ public final class ExoPlayerTest { Player.Commands defaultCommands = createWithDefaultCommands(); Player.Commands commandsWithSeekToNextWindow = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT); - Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true); + Player.Commands emptyTimelineCommands = + createWithDefaultCommands( + /* isTimelineEmpty= */ true, /* allowDeviceVolumeControl= */ false); Player.Listener mockListener = mock(Player.Listener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -9557,7 +9585,9 @@ public final class ExoPlayerTest { Player.Commands defaultCommands = createWithDefaultCommands(); Player.Commands commandsWithSeekToPreviousWindow = createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true); + Player.Commands emptyTimelineCommands = + createWithDefaultCommands( + /* isTimelineEmpty= */ true, /* allowDeviceVolumeControl= */ false); Player.Listener mockListener = mock(Player.Listener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -12323,7 +12353,9 @@ public final class ExoPlayerTest { @Test public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() { ExoPlayer player = - new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build(); + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(true) + .build(); Player.Listener listener = mock(Player.Listener.class); player.addListener(listener); @@ -12343,6 +12375,113 @@ public final class ExoPlayerTest { verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean()); } + @Test + public void setDeviceMutedWithoutDeviceVolumeControl_noEffectDeviceRemainsUnmuted() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.setDeviceMuted(/* muted= */ true, /* flags= */ 0); // no volume control, no effect + boolean isActuallyMuted = player.isDeviceMuted(); + player.release(); + + assertThat(isActuallyMuted).isFalse(); + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void setDeviceMutedWithDeviceVolumeControl_deviceGetsMuted() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(true) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.setDeviceMuted(/* muted= */ true, /* flags= */ 0); + boolean isActuallyMuted = player.isDeviceMuted(); + player.release(); + + assertThat(isActuallyMuted).isTrue(); + verify(listener).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void increaseDeviceVolumeWithoutDeviceVolumeControl_deviceVolumeUnchanged() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.increaseDeviceVolume(/* flags= */ 0); // no volume control, no effect + player.release(); + + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void decreaseDeviceVolumeWithoutDeviceVolumeControl_deviceVolumeUnchanged() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.decreaseDeviceVolume(/* flags= */ 0); // no volume control, no effect + player.release(); + + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void getDeviceVolumeWithoutDeviceVolumeControl_returnsZero() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + int initialDeviceVolume = player.getDeviceVolume(); + player.setDeviceVolume(10, /* flags= */ 0); + int setDeviceVolumeAt10 = player.getDeviceVolume(); + player.increaseDeviceVolume(/* flags= */ 0); + player.increaseDeviceVolume(/* flags= */ 0); + int setDeviceVolumeAt12 = player.getDeviceVolume(); + player.decreaseDeviceVolume(/* flags= */ 0); + int setDeviceVolumeAt11 = player.getDeviceVolume(); + player.release(); + + assertThat(initialDeviceVolume).isEqualTo(0); + assertThat(setDeviceVolumeAt10).isEqualTo(0); + assertThat(setDeviceVolumeAt12).isEqualTo(0); + assertThat(setDeviceVolumeAt11).isEqualTo(0); + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void getDeviceInfoWithoutDeviceVolumeControl_returnsZeroForMinMaxVolume() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + DeviceInfo deviceInfo = player.getDeviceInfo(); + int minVolume = deviceInfo.minVolume; + int maxVolume = deviceInfo.maxVolume; + + assertThat(minVolume).isEqualTo(0); + assertThat(maxVolume).isEqualTo(0); + } + @Test public void loadControlBackBuffer_withInsufficientMemoryLimits_stillContinuesPlayback() throws Exception { @@ -12482,8 +12621,11 @@ public final class ExoPlayerTest { return false; } + @SuppressWarnings("deprecation") // Control flow for the old volume commands private static Player.Commands createWithDefaultCommands( - boolean isTimelineEmpty, @Player.Command int... additionalCommands) { + boolean isTimelineEmpty, + boolean allowDeviceVolumeControl, + @Player.Command int... additionalCommands) { Player.Commands.Builder builder = new Player.Commands.Builder(); builder.addAll( COMMAND_PLAY_PAUSE, @@ -12502,12 +12644,7 @@ public final class ExoPlayerTest { COMMAND_SET_MEDIA_ITEM, COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, COMMAND_SET_VIDEO_SURFACE, COMMAND_GET_TEXT, COMMAND_SET_TRACK_SELECTION_PARAMETERS, @@ -12516,13 +12653,22 @@ public final class ExoPlayerTest { if (!isTimelineEmpty) { builder.add(COMMAND_SEEK_TO_PREVIOUS); } + if (allowDeviceVolumeControl) { + builder.addAll( + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS); + } builder.addAll(additionalCommands); return builder.build(); } private static Player.Commands createWithDefaultCommands( @Player.Command int... additionalCommands) { - return createWithDefaultCommands(/* isTimelineEmpty= */ false, additionalCommands); + return createWithDefaultCommands( + /* isTimelineEmpty= */ false, /* allowDeviceVolumeControl= */ false, additionalCommands); } // Internal classes. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index afc58e3ef8..2f9dda142c 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -54,6 +54,7 @@ public class TestExoPlayerBuilder { private @MonotonicNonNull Looper looper; private long seekBackIncrementMs; private long seekForwardIncrementMs; + private boolean deviceVolumeControlEnabled; public TestExoPlayerBuilder(Context context) { this.context = context; @@ -67,6 +68,7 @@ public class TestExoPlayerBuilder { } seekBackIncrementMs = C.DEFAULT_SEEK_BACK_INCREMENT_MS; seekForwardIncrementMs = C.DEFAULT_SEEK_FORWARD_INCREMENT_MS; + deviceVolumeControlEnabled = false; } /** @@ -282,6 +284,18 @@ public class TestExoPlayerBuilder { return this; } + /** + * Sets the variable controlling player's ability to get/set device volume. + * + * @param deviceVolumeControlEnabled Whether the player can get/set device volume. + * @return This builder. + */ + @CanIgnoreReturnValue + public TestExoPlayerBuilder setDeviceVolumeControlEnabled(boolean deviceVolumeControlEnabled) { + this.deviceVolumeControlEnabled = deviceVolumeControlEnabled; + return this; + } + /** Returns the seek forward increment used by the player. */ public long getSeekForwardIncrementMs() { return seekForwardIncrementMs; @@ -322,7 +336,8 @@ public class TestExoPlayerBuilder { .setUseLazyPreparation(useLazyPreparation) .setLooper(looper) .setSeekBackIncrementMs(seekBackIncrementMs) - .setSeekForwardIncrementMs(seekForwardIncrementMs); + .setSeekForwardIncrementMs(seekForwardIncrementMs) + .setDeviceVolumeControlEnabled(deviceVolumeControlEnabled); if (mediaSourceFactory != null) { builder.setMediaSourceFactory(mediaSourceFactory); }