diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index 921d5acf36..9803450241 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -1290,7 +1290,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; if (msg.what == MSG_PLAYER_INFO_CHANGED) { playerInfo = playerInfo.copyWithTimelineAndSessionPositionInfo( - getPlayerWrapper().getCurrentTimeline(), + getPlayerWrapper().getCurrentTimelineWithCommandCheck(), getPlayerWrapper().createSessionPositionInfoForBundling()); dispatchOnPlayerInfoChanged(playerInfo, excludeTimeline, excludeTracks); excludeTimeline = true; diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index 3c25022e9d..e130763ea2 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -23,7 +23,9 @@ import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; @@ -256,10 +258,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; || playbackState == STATE_ENDED || playbackState == STATE_IDLE) { if (playbackState == STATE_IDLE) { - playerWrapper.prepare(); + playerWrapper.prepareIfCommandAvailable(); } else if (playbackState == STATE_ENDED) { - playerWrapper.seekTo( - playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET); + playerWrapper.seekToDefaultPositionIfCommandAvailable(); } playerWrapper.play(); } else { @@ -308,10 +309,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper(); @Player.State int playbackState = playerWrapper.getPlaybackState(); if (playbackState == Player.STATE_IDLE) { - playerWrapper.prepare(); + playerWrapper.prepareIfCommandAvailable(); } else if (playbackState == Player.STATE_ENDED) { - playerWrapper.seekTo( - playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET); + playerWrapper.seekToDefaultPositionIfCommandAvailable(); } if (sessionImpl.onPlayRequested()) { playerWrapper.play(); @@ -369,18 +369,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public void onSkipToNext() { - dispatchSessionTaskWithPlayerCommand( - COMMAND_SEEK_TO_NEXT, - controller -> sessionImpl.getPlayerWrapper().seekToNext(), - sessionCompat.getCurrentControllerInfo()); + if (sessionImpl.getPlayerWrapper().isCommandAvailable(COMMAND_SEEK_TO_NEXT)) { + dispatchSessionTaskWithPlayerCommand( + COMMAND_SEEK_TO_NEXT, + controller -> sessionImpl.getPlayerWrapper().seekToNext(), + sessionCompat.getCurrentControllerInfo()); + } else { + dispatchSessionTaskWithPlayerCommand( + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + controller -> sessionImpl.getPlayerWrapper().seekToNextMediaItem(), + sessionCompat.getCurrentControllerInfo()); + } } @Override public void onSkipToPrevious() { - dispatchSessionTaskWithPlayerCommand( - COMMAND_SEEK_TO_PREVIOUS, - controller -> sessionImpl.getPlayerWrapper().seekToPrevious(), - sessionCompat.getCurrentControllerInfo()); + if (sessionImpl.getPlayerWrapper().isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)) { + dispatchSessionTaskWithPlayerCommand( + COMMAND_SEEK_TO_PREVIOUS, + controller -> sessionImpl.getPlayerWrapper().seekToPrevious(), + sessionCompat.getCurrentControllerInfo()); + } else { + dispatchSessionTaskWithPlayerCommand( + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, + controller -> sessionImpl.getPlayerWrapper().seekToPreviousMediaItem(), + sessionCompat.getCurrentControllerInfo()); + } } @Override @@ -435,7 +449,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; dispatchSessionTaskWithSessionCommand( SessionCommand.COMMAND_CODE_SESSION_SET_RATING, controller -> { - @Nullable MediaItem currentItem = sessionImpl.getPlayerWrapper().getCurrentMediaItem(); + @Nullable + MediaItem currentItem = + sessionImpl.getPlayerWrapper().getCurrentMediaItemWithCommandCheck(); if (currentItem == null) { return; } @@ -494,12 +510,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; Log.w(TAG, "onRemoveQueueItem(): Media ID shouldn't be null"); return; } - Timeline timeline = sessionImpl.getPlayerWrapper().getCurrentTimeline(); + PlayerWrapper player = sessionImpl.getPlayerWrapper(); + if (!player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)) { + Log.w(TAG, "Can't remove item by id without availabe COMMAND_GET_TIMELINE"); + return; + } + Timeline timeline = player.getCurrentTimeline(); Timeline.Window window = new Timeline.Window(); for (int i = 0; i < timeline.getWindowCount(); i++) { MediaItem mediaItem = timeline.getWindow(i, window).mediaItem; if (TextUtils.equals(mediaItem.mediaId, mediaId)) { - sessionImpl.getPlayerWrapper().removeMediaItem(i); + player.removeMediaItem(i); return; } } @@ -700,16 +721,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; postOrRun( sessionImpl.getApplicationHandler(), () -> { - Player player = sessionImpl.getPlayerWrapper(); + PlayerWrapper player = sessionImpl.getPlayerWrapper(); player.setMediaItems(mediaItems); @Player.State int playbackState = player.getPlaybackState(); if (playbackState == Player.STATE_IDLE) { - player.prepare(); + player.prepareIfCommandAvailable(); } else if (playbackState == Player.STATE_ENDED) { - player.seekTo(/* positionMs= */ C.TIME_UNSET); + player.seekToDefaultPositionIfCommandAvailable(); } if (play) { - player.play(); + player.playIfCommandAvailable(); } }); } @@ -875,19 +896,21 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; throws RemoteException { // Tells the playlist change first, so current media item index change notification // can point to the valid current media item in the playlist. - Timeline newTimeline = newPlayerWrapper.getCurrentTimeline(); + Timeline newTimeline = newPlayerWrapper.getCurrentTimelineWithCommandCheck(); if (oldPlayerWrapper == null - || !Util.areEqual(oldPlayerWrapper.getCurrentTimeline(), newTimeline)) { + || !Util.areEqual(oldPlayerWrapper.getCurrentTimelineWithCommandCheck(), newTimeline)) { onTimelineChanged(seq, newTimeline, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); } - MediaMetadata newPlaylistMetadata = newPlayerWrapper.getPlaylistMetadata(); + MediaMetadata newPlaylistMetadata = newPlayerWrapper.getPlaylistMetadataWithCommandCheck(); if (oldPlayerWrapper == null - || !Util.areEqual(oldPlayerWrapper.getPlaylistMetadata(), newPlaylistMetadata)) { + || !Util.areEqual( + oldPlayerWrapper.getPlaylistMetadataWithCommandCheck(), newPlaylistMetadata)) { onPlaylistMetadataChanged(seq, newPlaylistMetadata); } - MediaMetadata newMediaMetadata = newPlayerWrapper.getMediaMetadata(); + MediaMetadata newMediaMetadata = newPlayerWrapper.getMediaMetadataWithCommandCheck(); if (oldPlayerWrapper == null - || !Util.areEqual(oldPlayerWrapper.getMediaMetadata(), newMediaMetadata)) { + || !Util.areEqual( + oldPlayerWrapper.getMediaMetadataWithCommandCheck(), newMediaMetadata)) { onMediaMetadataChanged(seq, newMediaMetadata); } if (oldPlayerWrapper == null @@ -904,9 +927,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; onDeviceInfoChanged(seq, newPlayerWrapper.getDeviceInfo()); // Rest of changes are all notified via PlaybackStateCompat. - @Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItem(); + @Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItemWithCommandCheck(); if (oldPlayerWrapper == null - || !Util.areEqual(oldPlayerWrapper.getCurrentMediaItem(), newMediaItem)) { + || !Util.areEqual(oldPlayerWrapper.getCurrentMediaItemWithCommandCheck(), newMediaItem)) { // Note: This will update both PlaybackStateCompat and metadata. onMediaItemTransition( seq, newMediaItem, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); @@ -1135,7 +1158,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; PlayerWrapper player = sessionImpl.getPlayerWrapper(); volumeProviderCompat = player.createVolumeProviderCompat(); if (volumeProviderCompat == null) { - int streamType = MediaUtils.getLegacyStreamType(player.getAudioAttributes()); + int streamType = + MediaUtils.getLegacyStreamType(player.getAudioAttributesWithCommandCheck()); sessionCompat.setPlaybackToLocal(streamType); } else { sessionCompat.setPlaybackToRemote(volumeProviderCompat); @@ -1158,10 +1182,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } private void updateMetadataIfChanged() { - Player player = sessionImpl.getPlayerWrapper(); - @Nullable MediaItem currentMediaItem = player.getCurrentMediaItem(); - MediaMetadata newMediaMetadata = player.getMediaMetadata(); - long newDurationMs = player.getDuration(); + PlayerWrapper player = sessionImpl.getPlayerWrapper(); + @Nullable MediaItem currentMediaItem = player.getCurrentMediaItemWithCommandCheck(); + MediaMetadata newMediaMetadata = player.getMediaMetadataWithCommandCheck(); + long newDurationMs = player.getDurationWithCommandCheck(); String newMediaId = currentMediaItem != null ? currentMediaItem.mediaId : MediaItem.DEFAULT_MEDIA_ID; @Nullable diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java index a9eab3f1a3..97a85e3ffb 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java @@ -26,6 +26,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; +import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; import android.view.Surface; import android.view.SurfaceHolder; @@ -34,6 +35,7 @@ import android.view.TextureView; import androidx.annotation.Nullable; import androidx.media.VolumeProviderCompat; import androidx.media3.common.AudioAttributes; +import androidx.media3.common.C; import androidx.media3.common.DeviceInfo; import androidx.media3.common.ForwardingPlayer; import androidx.media3.common.MediaItem; @@ -43,9 +45,11 @@ import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; import androidx.media3.common.Timeline; import androidx.media3.common.TrackSelectionParameters; +import androidx.media3.common.Tracks; import androidx.media3.common.VideoSize; import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.Log; +import androidx.media3.common.util.Size; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; import java.util.List; @@ -133,6 +137,12 @@ import java.util.List; super.play(); } + public void playIfCommandAvailable() { + if (isCommandAvailable(COMMAND_PLAY_PAUSE)) { + play(); + } + } + @Override public void pause() { verifyApplicationThread(); @@ -145,6 +155,12 @@ import java.util.List; super.prepare(); } + public void prepareIfCommandAvailable() { + if (isCommandAvailable(COMMAND_PREPARE)) { + prepare(); + } + } + @Override public void stop() { verifyApplicationThread(); @@ -163,6 +179,18 @@ import java.util.List; super.seekToDefaultPosition(mediaItemIndex); } + @Override + public void seekToDefaultPosition() { + verifyApplicationThread(); + super.seekToDefaultPosition(); + } + + public void seekToDefaultPositionIfCommandAvailable() { + if (isCommandAvailable(Player.COMMAND_SEEK_TO_DEFAULT_POSITION)) { + seekToDefaultPosition(); + } + } + @Override public void seekTo(long positionMs) { verifyApplicationThread(); @@ -223,6 +251,10 @@ import java.util.List; return super.getDuration(); } + public long getDurationWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM) ? getDuration() : C.TIME_UNSET; + } + @Override public long getBufferedPosition() { verifyApplicationThread(); @@ -355,6 +387,12 @@ import java.util.List; return super.getAudioAttributes(); } + public AudioAttributes getAudioAttributesWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES) + ? getAudioAttributes() + : AudioAttributes.DEFAULT; + } + @Override public void setMediaItem(MediaItem mediaItem) { verifyApplicationThread(); @@ -549,12 +587,22 @@ import java.util.List; return super.getCurrentTimeline(); } + public Timeline getCurrentTimelineWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_TIMELINE) ? getCurrentTimeline() : Timeline.EMPTY; + } + @Override public MediaMetadata getPlaylistMetadata() { verifyApplicationThread(); return super.getPlaylistMetadata(); } + public MediaMetadata getPlaylistMetadataWithCommandCheck() { + return isCommandAvailable(Player.COMMAND_GET_MEDIA_ITEMS_METADATA) + ? getPlaylistMetadata() + : MediaMetadata.EMPTY; + } + @Override public int getRepeatMode() { verifyApplicationThread(); @@ -574,6 +622,11 @@ import java.util.List; return super.getCurrentMediaItem(); } + @Nullable + public MediaItem getCurrentMediaItemWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM) ? getCurrentMediaItem() : null; + } + @Override public int getMediaItemCount() { verifyApplicationThread(); @@ -631,6 +684,10 @@ import java.util.List; return super.getVolume(); } + public float getVolumeWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_VOLUME) ? getVolume() : 0; + } + @Override public void setVolume(float volume) { verifyApplicationThread(); @@ -643,6 +700,10 @@ import java.util.List; return super.getCurrentCues(); } + public CueGroup getCurrentCuesWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_TEXT) ? getCurrentCues() : CueGroup.EMPTY_TIME_ZERO; + } + @Override public DeviceInfo getDeviceInfo() { verifyApplicationThread(); @@ -655,18 +716,32 @@ import java.util.List; return super.getDeviceVolume(); } + public int getDeviceVolumeWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_DEVICE_VOLUME) ? getDeviceVolume() : 0; + } + @Override public boolean isDeviceMuted() { verifyApplicationThread(); return super.isDeviceMuted(); } + public boolean isDeviceMutedWithCommandCheck() { + return isCommandAvailable(Player.COMMAND_GET_DEVICE_VOLUME) && isDeviceMuted(); + } + @Override public void setDeviceVolume(int volume) { verifyApplicationThread(); super.setDeviceVolume(volume); } + public void setDeviceVolumeIfCommandAvailable(int volume) { + if (isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)) { + setDeviceVolume(volume); + } + } + @Override public void increaseDeviceVolume() { verifyApplicationThread(); @@ -729,6 +804,12 @@ import java.util.List; return super.getMediaMetadata(); } + public MediaMetadata getMediaMetadataWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA) + ? getMediaMetadata() + : MediaMetadata.EMPTY; + } + @Override public boolean isCommandAvailable(@Command int command) { verifyApplicationThread(); @@ -753,6 +834,71 @@ import java.util.List; super.setTrackSelectionParameters(parameters); } + @Override + public void seekToPrevious() { + verifyApplicationThread(); + super.seekToPrevious(); + } + + @Override + public long getMaxSeekToPreviousPosition() { + verifyApplicationThread(); + return super.getMaxSeekToPreviousPosition(); + } + + @Override + public void seekToNext() { + verifyApplicationThread(); + super.seekToNext(); + } + + @Override + public Tracks getCurrentTracks() { + verifyApplicationThread(); + return super.getCurrentTracks(); + } + + public Tracks getCurrentTracksWithCommandCheck() { + return isCommandAvailable(COMMAND_GET_TRACKS) ? getCurrentTracks() : Tracks.EMPTY; + } + + @Nullable + @Override + public Object getCurrentManifest() { + verifyApplicationThread(); + return super.getCurrentManifest(); + } + + @Override + public int getCurrentPeriodIndex() { + verifyApplicationThread(); + return super.getCurrentPeriodIndex(); + } + + @Override + public boolean isCurrentMediaItemDynamic() { + verifyApplicationThread(); + return super.isCurrentMediaItemDynamic(); + } + + @Override + public boolean isCurrentMediaItemLive() { + verifyApplicationThread(); + return super.isCurrentMediaItemLive(); + } + + @Override + public boolean isCurrentMediaItemSeekable() { + verifyApplicationThread(); + return super.isCurrentMediaItemSeekable(); + } + + @Override + public Size getSurfaceSize() { + verifyApplicationThread(); + return super.getSurfaceSize(); + } + public PlaybackStateCompat createPlaybackStateCompat() { if (legacyStatusCode != STATUS_CODE_SUCCESS_COMPAT) { return new PlaybackStateCompat.Builder() @@ -799,22 +945,28 @@ import java.util.List; || getAvailableCommands().contains(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)) { allActions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } - long queueItemId = MediaUtils.convertToQueueItemId(getCurrentMediaItemIndex()); + long queueItemId = + isCommandAvailable(COMMAND_GET_TIMELINE) + ? MediaUtils.convertToQueueItemId(getCurrentMediaItemIndex()) + : MediaSessionCompat.QueueItem.UNKNOWN_ID; float playbackSpeed = getPlaybackParameters().speed; float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f; Bundle extras = new Bundle(); extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed); - @Nullable MediaItem currentMediaItem = getCurrentMediaItem(); + @Nullable MediaItem currentMediaItem = getCurrentMediaItemWithCommandCheck(); if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) { extras.putString(EXTRAS_KEY_MEDIA_ID_COMPAT, currentMediaItem.mediaId); } + boolean canReadPositions = isCommandAvailable(Player.COMMAND_GET_CURRENT_MEDIA_ITEM); + long compatPosition = + canReadPositions ? getCurrentPosition() : PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN; + long compatBufferedPosition = canReadPositions ? getBufferedPosition() : 0; PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() - .setState( - state, getCurrentPosition(), sessionPlaybackSpeed, SystemClock.elapsedRealtime()) + .setState(state, compatPosition, sessionPlaybackSpeed, SystemClock.elapsedRealtime()) .setActions(allActions) .setActiveQueueItemId(queueItemId) - .setBufferedPosition(getBufferedPosition()) + .setBufferedPosition(compatBufferedPosition) .setExtras(extras); for (int i = 0; i < customLayout.size(); i++) { @@ -853,11 +1005,11 @@ import java.util.List; } } Handler handler = new Handler(getApplicationLooper()); - return new VolumeProviderCompat( - volumeControlType, getDeviceInfo().maxVolume, getDeviceVolume()) { + int currentVolume = getDeviceVolumeWithCommandCheck(); + return new VolumeProviderCompat(volumeControlType, getDeviceInfo().maxVolume, currentVolume) { @Override public void onSetVolumeTo(int volume) { - postOrRun(handler, () -> setDeviceVolume(volume)); + postOrRun(handler, () -> setDeviceVolumeIfCommandAvailable(volume)); } @Override @@ -865,6 +1017,9 @@ import java.util.List; postOrRun( handler, () -> { + if (!isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)) { + return; + } switch (direction) { case AudioManager.ADJUST_RAISE: increaseDeviceVolume(); @@ -879,7 +1034,7 @@ import java.util.List; setDeviceMuted(false); break; case AudioManager.ADJUST_TOGGLE_MUTE: - setDeviceMuted(!isDeviceMuted()); + setDeviceMuted(!isDeviceMutedWithCommandCheck()); break; default: Log.w( @@ -898,6 +1053,9 @@ import java.util.List; *
This excludes window uid and period uid that wouldn't be preserved when bundling. */ public PositionInfo createPositionInfoForBundling() { + if (!isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) { + return SessionPositionInfo.DEFAULT_POSITION_INFO; + } return new PositionInfo( /* windowUid= */ null, getCurrentMediaItemIndex(), @@ -916,6 +1074,9 @@ import java.util.List; *
This excludes window uid and period uid that wouldn't be preserved when bundling.
*/
public SessionPositionInfo createSessionPositionInfoForBundling() {
+ if (!isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) {
+ return SessionPositionInfo.DEFAULT;
+ }
return new SessionPositionInfo(
createPositionInfoForBundling(),
isPlayingAd(),
@@ -941,25 +1102,25 @@ import java.util.List;
getRepeatMode(),
getShuffleModeEnabled(),
getVideoSize(),
- getCurrentTimeline(),
- getPlaylistMetadata(),
- getVolume(),
- getAudioAttributes(),
- getCurrentCues(),
+ getCurrentTimelineWithCommandCheck(),
+ getPlaylistMetadataWithCommandCheck(),
+ getVolumeWithCommandCheck(),
+ getAudioAttributesWithCommandCheck(),
+ getCurrentCuesWithCommandCheck(),
getDeviceInfo(),
- getDeviceVolume(),
- isDeviceMuted(),
+ getDeviceVolumeWithCommandCheck(),
+ isDeviceMutedWithCommandCheck(),
getPlayWhenReady(),
PlayerInfo.PLAY_WHEN_READY_CHANGE_REASON_DEFAULT,
getPlaybackSuppressionReason(),
getPlaybackState(),
isPlaying(),
isLoading(),
- getMediaMetadata(),
+ getMediaMetadataWithCommandCheck(),
getSeekBackIncrement(),
getSeekForwardIncrement(),
getMaxSeekToPreviousPosition(),
- getCurrentTracks(),
+ getCurrentTracksWithCommandCheck(),
getTrackSelectionParameters());
}
diff --git a/libraries/session/src/test/java/androidx/media3/session/PlayerWrapperTest.java b/libraries/session/src/test/java/androidx/media3/session/PlayerWrapperTest.java
index 85e26f4cc3..430e14c61c 100644
--- a/libraries/session/src/test/java/androidx/media3/session/PlayerWrapperTest.java
+++ b/libraries/session/src/test/java/androidx/media3/session/PlayerWrapperTest.java
@@ -16,6 +16,7 @@
package androidx.media3.session;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.os.Looper;
@@ -42,6 +43,7 @@ public class PlayerWrapperTest {
@Before
public void setUp() {
playerWrapper = new PlayerWrapper(player);
+ when(player.isCommandAvailable(anyInt())).thenReturn(true);
when(player.getApplicationLooper()).thenReturn(Looper.myLooper());
}
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java
index 6fd12bc37e..504c48ed63 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackWithMediaControllerCompatTest.java
@@ -18,7 +18,9 @@ package androidx.media3.session;
import static androidx.media.MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE;
import static androidx.media3.common.Player.COMMAND_PREPARE;
+import static androidx.media3.common.Player.STATE_ENDED;
import static androidx.media3.common.Player.STATE_IDLE;
+import static androidx.media3.common.Player.STATE_READY;
import static androidx.media3.session.SessionResult.RESULT_ERROR_INVALID_STATE;
import static androidx.media3.session.SessionResult.RESULT_SUCCESS;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
@@ -204,7 +206,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
}
@Test
- public void play() throws Exception {
+ public void play_whileReady_callsPlay() throws Exception {
+ player.playbackState = STATE_READY;
session =
new MediaSession.Builder(context, player)
.setId("play")
@@ -217,6 +220,92 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller.getTransportControls().play();
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION)).isFalse();
+ }
+
+ @Test
+ public void play_whileIdle_callsPrepareAndPlay() throws Exception {
+ player.playbackState = STATE_IDLE;
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("play")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().play();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
+ player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION)).isFalse();
+ }
+
+ @Test
+ public void play_whileIdleWithoutPrepareCommandAvailable_callsJustPlay() throws Exception {
+ player.playbackState = STATE_IDLE;
+ player.commands =
+ new Player.Commands.Builder().addAllCommands().remove(Player.COMMAND_PREPARE).build();
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("play")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().play();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION)).isFalse();
+ }
+
+ @Test
+ public void play_whileEnded_callsSeekToDefaultPositionAndPlay() throws Exception {
+ player.playbackState = STATE_ENDED;
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("play")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().play();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION, TIMEOUT_MS);
+ player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
+ }
+
+ @Test
+ public void play_whileEndedWithoutSeekToDefaultPositionCommandAvailable_callsJustPlay()
+ throws Exception {
+ player.playbackState = STATE_ENDED;
+ player.commands =
+ new Player.Commands.Builder()
+ .addAllCommands()
+ .remove(Player.COMMAND_SEEK_TO_DEFAULT_POSITION)
+ .build();
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("play")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().play();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION)).isFalse();
+ assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
}
@Test
@@ -428,7 +517,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
}
@Test
- public void skipToPrevious() throws Exception {
+ public void skipToPrevious_withAllCommandsAvailable_callsSeekToPrevious() throws Exception {
session =
new MediaSession.Builder(context, player)
.setId("skipToPrevious")
@@ -444,7 +533,29 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
}
@Test
- public void skipToNext() throws Exception {
+ public void skipToPrevious_withoutSeekToPreviousCommandAvailable_callsSeekToPreviousMediaItem()
+ throws Exception {
+ player.commands =
+ new Player.Commands.Builder()
+ .addAllCommands()
+ .remove(Player.COMMAND_SEEK_TO_PREVIOUS)
+ .build();
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("skipToPrevious")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().skipToPrevious();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_PREVIOUS_MEDIA_ITEM, TIMEOUT_MS);
+ }
+
+ @Test
+ public void skipToNext_withAllCommandsAvailable_callsSeekToNext() throws Exception {
session =
new MediaSession.Builder(context, player)
.setId("skipToNext")
@@ -459,6 +570,25 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT, TIMEOUT_MS);
}
+ @Test
+ public void skipToNext_withoutSeekToNextCommandAvailable_callsSeekToNextMediaItem()
+ throws Exception {
+ player.commands =
+ new Player.Commands.Builder().addAllCommands().remove(Player.COMMAND_SEEK_TO_NEXT).build();
+ session =
+ new MediaSession.Builder(context, player)
+ .setId("skipToNext")
+ .setCallback(new TestSessionCallback())
+ .build();
+ controller =
+ new RemoteMediaControllerCompat(
+ context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
+
+ controller.getTransportControls().skipToNext();
+
+ player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_NEXT_MEDIA_ITEM, TIMEOUT_MS);
+ }
+
@Test
public void skipToQueueItem() throws Exception {
session =
@@ -1049,6 +1179,101 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
}
+ @Test
+ public void prepareFromMediaUri_withoutAvailablePrepareCommand_justCallsSetMediaItems()
+ throws Exception {
+ MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
+ MediaSession.Callback callback =
+ new MediaSession.Callback() {
+ @Override
+ public ListenableFuture> onAddMediaItems(
+ MediaSession mediaSession, ControllerInfo controller, List
> onAddMediaItems(
+ MediaSession mediaSession, ControllerInfo controller, List
> onAddMediaItems(
+ MediaSession mediaSession, ControllerInfo controller, List