Commit a2a44cdc by tonihei Committed by Rohit Singh

Add missing command checks to MediaSessionLegacyStub and PlayerWrapper

This player didn't fully check all player commands before calling the
respective methods.

PiperOrigin-RevId: 502353704
parent 664ab72d
...@@ -1290,7 +1290,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; ...@@ -1290,7 +1290,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
if (msg.what == MSG_PLAYER_INFO_CHANGED) { if (msg.what == MSG_PLAYER_INFO_CHANGED) {
playerInfo = playerInfo =
playerInfo.copyWithTimelineAndSessionPositionInfo( playerInfo.copyWithTimelineAndSessionPositionInfo(
getPlayerWrapper().getCurrentTimeline(), getPlayerWrapper().getCurrentTimelineWithCommandCheck(),
getPlayerWrapper().createSessionPositionInfoForBundling()); getPlayerWrapper().createSessionPositionInfoForBundling());
dispatchOnPlayerInfoChanged(playerInfo, excludeTimeline, excludeTracks); dispatchOnPlayerInfoChanged(playerInfo, excludeTimeline, excludeTracks);
excludeTimeline = true; excludeTimeline = true;
......
...@@ -23,7 +23,9 @@ import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; ...@@ -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_IN_CURRENT_MEDIA_ITEM;
import static androidx.media3.common.Player.COMMAND_SEEK_TO_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;
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;
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_MEDIA_ITEM;
import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE; import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE;
import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE; import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE;
...@@ -256,10 +258,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -256,10 +258,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|| playbackState == STATE_ENDED || playbackState == STATE_ENDED
|| playbackState == STATE_IDLE) { || playbackState == STATE_IDLE) {
if (playbackState == STATE_IDLE) { if (playbackState == STATE_IDLE) {
playerWrapper.prepare(); playerWrapper.prepareIfCommandAvailable();
} else if (playbackState == STATE_ENDED) { } else if (playbackState == STATE_ENDED) {
playerWrapper.seekTo( playerWrapper.seekToDefaultPositionIfCommandAvailable();
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
} }
playerWrapper.play(); playerWrapper.play();
} else { } else {
...@@ -308,10 +309,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -308,10 +309,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper(); PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
@Player.State int playbackState = playerWrapper.getPlaybackState(); @Player.State int playbackState = playerWrapper.getPlaybackState();
if (playbackState == Player.STATE_IDLE) { if (playbackState == Player.STATE_IDLE) {
playerWrapper.prepare(); playerWrapper.prepareIfCommandAvailable();
} else if (playbackState == Player.STATE_ENDED) { } else if (playbackState == Player.STATE_ENDED) {
playerWrapper.seekTo( playerWrapper.seekToDefaultPositionIfCommandAvailable();
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
} }
if (sessionImpl.onPlayRequested()) { if (sessionImpl.onPlayRequested()) {
playerWrapper.play(); playerWrapper.play();
...@@ -369,18 +369,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -369,18 +369,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
@Override @Override
public void onSkipToNext() { public void onSkipToNext() {
if (sessionImpl.getPlayerWrapper().isCommandAvailable(COMMAND_SEEK_TO_NEXT)) {
dispatchSessionTaskWithPlayerCommand( dispatchSessionTaskWithPlayerCommand(
COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT,
controller -> sessionImpl.getPlayerWrapper().seekToNext(), controller -> sessionImpl.getPlayerWrapper().seekToNext(),
sessionCompat.getCurrentControllerInfo()); sessionCompat.getCurrentControllerInfo());
} else {
dispatchSessionTaskWithPlayerCommand(
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
controller -> sessionImpl.getPlayerWrapper().seekToNextMediaItem(),
sessionCompat.getCurrentControllerInfo());
}
} }
@Override @Override
public void onSkipToPrevious() { public void onSkipToPrevious() {
if (sessionImpl.getPlayerWrapper().isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)) {
dispatchSessionTaskWithPlayerCommand( dispatchSessionTaskWithPlayerCommand(
COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS,
controller -> sessionImpl.getPlayerWrapper().seekToPrevious(), controller -> sessionImpl.getPlayerWrapper().seekToPrevious(),
sessionCompat.getCurrentControllerInfo()); sessionCompat.getCurrentControllerInfo());
} else {
dispatchSessionTaskWithPlayerCommand(
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
controller -> sessionImpl.getPlayerWrapper().seekToPreviousMediaItem(),
sessionCompat.getCurrentControllerInfo());
}
} }
@Override @Override
...@@ -435,7 +449,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -435,7 +449,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
dispatchSessionTaskWithSessionCommand( dispatchSessionTaskWithSessionCommand(
SessionCommand.COMMAND_CODE_SESSION_SET_RATING, SessionCommand.COMMAND_CODE_SESSION_SET_RATING,
controller -> { controller -> {
@Nullable MediaItem currentItem = sessionImpl.getPlayerWrapper().getCurrentMediaItem(); @Nullable
MediaItem currentItem =
sessionImpl.getPlayerWrapper().getCurrentMediaItemWithCommandCheck();
if (currentItem == null) { if (currentItem == null) {
return; return;
} }
...@@ -494,12 +510,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -494,12 +510,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Log.w(TAG, "onRemoveQueueItem(): Media ID shouldn't be null"); Log.w(TAG, "onRemoveQueueItem(): Media ID shouldn't be null");
return; 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(); Timeline.Window window = new Timeline.Window();
for (int i = 0; i < timeline.getWindowCount(); i++) { for (int i = 0; i < timeline.getWindowCount(); i++) {
MediaItem mediaItem = timeline.getWindow(i, window).mediaItem; MediaItem mediaItem = timeline.getWindow(i, window).mediaItem;
if (TextUtils.equals(mediaItem.mediaId, mediaId)) { if (TextUtils.equals(mediaItem.mediaId, mediaId)) {
sessionImpl.getPlayerWrapper().removeMediaItem(i); player.removeMediaItem(i);
return; return;
} }
} }
...@@ -700,16 +721,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -700,16 +721,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
postOrRun( postOrRun(
sessionImpl.getApplicationHandler(), sessionImpl.getApplicationHandler(),
() -> { () -> {
Player player = sessionImpl.getPlayerWrapper(); PlayerWrapper player = sessionImpl.getPlayerWrapper();
player.setMediaItems(mediaItems); player.setMediaItems(mediaItems);
@Player.State int playbackState = player.getPlaybackState(); @Player.State int playbackState = player.getPlaybackState();
if (playbackState == Player.STATE_IDLE) { if (playbackState == Player.STATE_IDLE) {
player.prepare(); player.prepareIfCommandAvailable();
} else if (playbackState == Player.STATE_ENDED) { } else if (playbackState == Player.STATE_ENDED) {
player.seekTo(/* positionMs= */ C.TIME_UNSET); player.seekToDefaultPositionIfCommandAvailable();
} }
if (play) { if (play) {
player.play(); player.playIfCommandAvailable();
} }
}); });
} }
...@@ -875,19 +896,21 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -875,19 +896,21 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
throws RemoteException { throws RemoteException {
// Tells the playlist change first, so current media item index change notification // Tells the playlist change first, so current media item index change notification
// can point to the valid current media item in the playlist. // can point to the valid current media item in the playlist.
Timeline newTimeline = newPlayerWrapper.getCurrentTimeline(); Timeline newTimeline = newPlayerWrapper.getCurrentTimelineWithCommandCheck();
if (oldPlayerWrapper == null if (oldPlayerWrapper == null
|| !Util.areEqual(oldPlayerWrapper.getCurrentTimeline(), newTimeline)) { || !Util.areEqual(oldPlayerWrapper.getCurrentTimelineWithCommandCheck(), newTimeline)) {
onTimelineChanged(seq, newTimeline, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); onTimelineChanged(seq, newTimeline, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
} }
MediaMetadata newPlaylistMetadata = newPlayerWrapper.getPlaylistMetadata(); MediaMetadata newPlaylistMetadata = newPlayerWrapper.getPlaylistMetadataWithCommandCheck();
if (oldPlayerWrapper == null if (oldPlayerWrapper == null
|| !Util.areEqual(oldPlayerWrapper.getPlaylistMetadata(), newPlaylistMetadata)) { || !Util.areEqual(
oldPlayerWrapper.getPlaylistMetadataWithCommandCheck(), newPlaylistMetadata)) {
onPlaylistMetadataChanged(seq, newPlaylistMetadata); onPlaylistMetadataChanged(seq, newPlaylistMetadata);
} }
MediaMetadata newMediaMetadata = newPlayerWrapper.getMediaMetadata(); MediaMetadata newMediaMetadata = newPlayerWrapper.getMediaMetadataWithCommandCheck();
if (oldPlayerWrapper == null if (oldPlayerWrapper == null
|| !Util.areEqual(oldPlayerWrapper.getMediaMetadata(), newMediaMetadata)) { || !Util.areEqual(
oldPlayerWrapper.getMediaMetadataWithCommandCheck(), newMediaMetadata)) {
onMediaMetadataChanged(seq, newMediaMetadata); onMediaMetadataChanged(seq, newMediaMetadata);
} }
if (oldPlayerWrapper == null if (oldPlayerWrapper == null
...@@ -904,9 +927,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -904,9 +927,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
onDeviceInfoChanged(seq, newPlayerWrapper.getDeviceInfo()); onDeviceInfoChanged(seq, newPlayerWrapper.getDeviceInfo());
// Rest of changes are all notified via PlaybackStateCompat. // Rest of changes are all notified via PlaybackStateCompat.
@Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItem(); @Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItemWithCommandCheck();
if (oldPlayerWrapper == null if (oldPlayerWrapper == null
|| !Util.areEqual(oldPlayerWrapper.getCurrentMediaItem(), newMediaItem)) { || !Util.areEqual(oldPlayerWrapper.getCurrentMediaItemWithCommandCheck(), newMediaItem)) {
// Note: This will update both PlaybackStateCompat and metadata. // Note: This will update both PlaybackStateCompat and metadata.
onMediaItemTransition( onMediaItemTransition(
seq, newMediaItem, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); seq, newMediaItem, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED);
...@@ -1135,7 +1158,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1135,7 +1158,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
PlayerWrapper player = sessionImpl.getPlayerWrapper(); PlayerWrapper player = sessionImpl.getPlayerWrapper();
volumeProviderCompat = player.createVolumeProviderCompat(); volumeProviderCompat = player.createVolumeProviderCompat();
if (volumeProviderCompat == null) { if (volumeProviderCompat == null) {
int streamType = MediaUtils.getLegacyStreamType(player.getAudioAttributes()); int streamType =
MediaUtils.getLegacyStreamType(player.getAudioAttributesWithCommandCheck());
sessionCompat.setPlaybackToLocal(streamType); sessionCompat.setPlaybackToLocal(streamType);
} else { } else {
sessionCompat.setPlaybackToRemote(volumeProviderCompat); sessionCompat.setPlaybackToRemote(volumeProviderCompat);
...@@ -1158,10 +1182,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1158,10 +1182,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
private void updateMetadataIfChanged() { private void updateMetadataIfChanged() {
Player player = sessionImpl.getPlayerWrapper(); PlayerWrapper player = sessionImpl.getPlayerWrapper();
@Nullable MediaItem currentMediaItem = player.getCurrentMediaItem(); @Nullable MediaItem currentMediaItem = player.getCurrentMediaItemWithCommandCheck();
MediaMetadata newMediaMetadata = player.getMediaMetadata(); MediaMetadata newMediaMetadata = player.getMediaMetadataWithCommandCheck();
long newDurationMs = player.getDuration(); long newDurationMs = player.getDurationWithCommandCheck();
String newMediaId = String newMediaId =
currentMediaItem != null ? currentMediaItem.mediaId : MediaItem.DEFAULT_MEDIA_ID; currentMediaItem != null ? currentMediaItem.mediaId : MediaItem.DEFAULT_MEDIA_ID;
@Nullable @Nullable
......
...@@ -26,6 +26,7 @@ import android.os.Bundle; ...@@ -26,6 +26,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat; import android.support.v4.media.session.PlaybackStateCompat;
import android.view.Surface; import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
...@@ -34,6 +35,7 @@ import android.view.TextureView; ...@@ -34,6 +35,7 @@ import android.view.TextureView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media.VolumeProviderCompat; import androidx.media.VolumeProviderCompat;
import androidx.media3.common.AudioAttributes; import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.DeviceInfo; import androidx.media3.common.DeviceInfo;
import androidx.media3.common.ForwardingPlayer; import androidx.media3.common.ForwardingPlayer;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
...@@ -43,9 +45,11 @@ import androidx.media3.common.PlaybackParameters; ...@@ -43,9 +45,11 @@ import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize; import androidx.media3.common.VideoSize;
import androidx.media3.common.text.CueGroup; import androidx.media3.common.text.CueGroup;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
...@@ -133,6 +137,12 @@ import java.util.List; ...@@ -133,6 +137,12 @@ import java.util.List;
super.play(); super.play();
} }
public void playIfCommandAvailable() {
if (isCommandAvailable(COMMAND_PLAY_PAUSE)) {
play();
}
}
@Override @Override
public void pause() { public void pause() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -145,6 +155,12 @@ import java.util.List; ...@@ -145,6 +155,12 @@ import java.util.List;
super.prepare(); super.prepare();
} }
public void prepareIfCommandAvailable() {
if (isCommandAvailable(COMMAND_PREPARE)) {
prepare();
}
}
@Override @Override
public void stop() { public void stop() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -164,6 +180,18 @@ import java.util.List; ...@@ -164,6 +180,18 @@ import java.util.List;
} }
@Override @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) { public void seekTo(long positionMs) {
verifyApplicationThread(); verifyApplicationThread();
super.seekTo(positionMs); super.seekTo(positionMs);
...@@ -223,6 +251,10 @@ import java.util.List; ...@@ -223,6 +251,10 @@ import java.util.List;
return super.getDuration(); return super.getDuration();
} }
public long getDurationWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM) ? getDuration() : C.TIME_UNSET;
}
@Override @Override
public long getBufferedPosition() { public long getBufferedPosition() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -355,6 +387,12 @@ import java.util.List; ...@@ -355,6 +387,12 @@ import java.util.List;
return super.getAudioAttributes(); return super.getAudioAttributes();
} }
public AudioAttributes getAudioAttributesWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)
? getAudioAttributes()
: AudioAttributes.DEFAULT;
}
@Override @Override
public void setMediaItem(MediaItem mediaItem) { public void setMediaItem(MediaItem mediaItem) {
verifyApplicationThread(); verifyApplicationThread();
...@@ -549,12 +587,22 @@ import java.util.List; ...@@ -549,12 +587,22 @@ import java.util.List;
return super.getCurrentTimeline(); return super.getCurrentTimeline();
} }
public Timeline getCurrentTimelineWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_TIMELINE) ? getCurrentTimeline() : Timeline.EMPTY;
}
@Override @Override
public MediaMetadata getPlaylistMetadata() { public MediaMetadata getPlaylistMetadata() {
verifyApplicationThread(); verifyApplicationThread();
return super.getPlaylistMetadata(); return super.getPlaylistMetadata();
} }
public MediaMetadata getPlaylistMetadataWithCommandCheck() {
return isCommandAvailable(Player.COMMAND_GET_MEDIA_ITEMS_METADATA)
? getPlaylistMetadata()
: MediaMetadata.EMPTY;
}
@Override @Override
public int getRepeatMode() { public int getRepeatMode() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -574,6 +622,11 @@ import java.util.List; ...@@ -574,6 +622,11 @@ import java.util.List;
return super.getCurrentMediaItem(); return super.getCurrentMediaItem();
} }
@Nullable
public MediaItem getCurrentMediaItemWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM) ? getCurrentMediaItem() : null;
}
@Override @Override
public int getMediaItemCount() { public int getMediaItemCount() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -631,6 +684,10 @@ import java.util.List; ...@@ -631,6 +684,10 @@ import java.util.List;
return super.getVolume(); return super.getVolume();
} }
public float getVolumeWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_VOLUME) ? getVolume() : 0;
}
@Override @Override
public void setVolume(float volume) { public void setVolume(float volume) {
verifyApplicationThread(); verifyApplicationThread();
...@@ -643,6 +700,10 @@ import java.util.List; ...@@ -643,6 +700,10 @@ import java.util.List;
return super.getCurrentCues(); return super.getCurrentCues();
} }
public CueGroup getCurrentCuesWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_TEXT) ? getCurrentCues() : CueGroup.EMPTY_TIME_ZERO;
}
@Override @Override
public DeviceInfo getDeviceInfo() { public DeviceInfo getDeviceInfo() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -655,18 +716,32 @@ import java.util.List; ...@@ -655,18 +716,32 @@ import java.util.List;
return super.getDeviceVolume(); return super.getDeviceVolume();
} }
public int getDeviceVolumeWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_DEVICE_VOLUME) ? getDeviceVolume() : 0;
}
@Override @Override
public boolean isDeviceMuted() { public boolean isDeviceMuted() {
verifyApplicationThread(); verifyApplicationThread();
return super.isDeviceMuted(); return super.isDeviceMuted();
} }
public boolean isDeviceMutedWithCommandCheck() {
return isCommandAvailable(Player.COMMAND_GET_DEVICE_VOLUME) && isDeviceMuted();
}
@Override @Override
public void setDeviceVolume(int volume) { public void setDeviceVolume(int volume) {
verifyApplicationThread(); verifyApplicationThread();
super.setDeviceVolume(volume); super.setDeviceVolume(volume);
} }
public void setDeviceVolumeIfCommandAvailable(int volume) {
if (isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)) {
setDeviceVolume(volume);
}
}
@Override @Override
public void increaseDeviceVolume() { public void increaseDeviceVolume() {
verifyApplicationThread(); verifyApplicationThread();
...@@ -729,6 +804,12 @@ import java.util.List; ...@@ -729,6 +804,12 @@ import java.util.List;
return super.getMediaMetadata(); return super.getMediaMetadata();
} }
public MediaMetadata getMediaMetadataWithCommandCheck() {
return isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)
? getMediaMetadata()
: MediaMetadata.EMPTY;
}
@Override @Override
public boolean isCommandAvailable(@Command int command) { public boolean isCommandAvailable(@Command int command) {
verifyApplicationThread(); verifyApplicationThread();
...@@ -753,6 +834,71 @@ import java.util.List; ...@@ -753,6 +834,71 @@ import java.util.List;
super.setTrackSelectionParameters(parameters); 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() { public PlaybackStateCompat createPlaybackStateCompat() {
if (legacyStatusCode != STATUS_CODE_SUCCESS_COMPAT) { if (legacyStatusCode != STATUS_CODE_SUCCESS_COMPAT) {
return new PlaybackStateCompat.Builder() return new PlaybackStateCompat.Builder()
...@@ -799,22 +945,28 @@ import java.util.List; ...@@ -799,22 +945,28 @@ import java.util.List;
|| getAvailableCommands().contains(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)) { || getAvailableCommands().contains(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)) {
allActions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; 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 playbackSpeed = getPlaybackParameters().speed;
float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f; float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f;
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed); 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)) { if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) {
extras.putString(EXTRAS_KEY_MEDIA_ID_COMPAT, 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 = PlaybackStateCompat.Builder builder =
new PlaybackStateCompat.Builder() new PlaybackStateCompat.Builder()
.setState( .setState(state, compatPosition, sessionPlaybackSpeed, SystemClock.elapsedRealtime())
state, getCurrentPosition(), sessionPlaybackSpeed, SystemClock.elapsedRealtime())
.setActions(allActions) .setActions(allActions)
.setActiveQueueItemId(queueItemId) .setActiveQueueItemId(queueItemId)
.setBufferedPosition(getBufferedPosition()) .setBufferedPosition(compatBufferedPosition)
.setExtras(extras); .setExtras(extras);
for (int i = 0; i < customLayout.size(); i++) { for (int i = 0; i < customLayout.size(); i++) {
...@@ -853,11 +1005,11 @@ import java.util.List; ...@@ -853,11 +1005,11 @@ import java.util.List;
} }
} }
Handler handler = new Handler(getApplicationLooper()); Handler handler = new Handler(getApplicationLooper());
return new VolumeProviderCompat( int currentVolume = getDeviceVolumeWithCommandCheck();
volumeControlType, getDeviceInfo().maxVolume, getDeviceVolume()) { return new VolumeProviderCompat(volumeControlType, getDeviceInfo().maxVolume, currentVolume) {
@Override @Override
public void onSetVolumeTo(int volume) { public void onSetVolumeTo(int volume) {
postOrRun(handler, () -> setDeviceVolume(volume)); postOrRun(handler, () -> setDeviceVolumeIfCommandAvailable(volume));
} }
@Override @Override
...@@ -865,6 +1017,9 @@ import java.util.List; ...@@ -865,6 +1017,9 @@ import java.util.List;
postOrRun( postOrRun(
handler, handler,
() -> { () -> {
if (!isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)) {
return;
}
switch (direction) { switch (direction) {
case AudioManager.ADJUST_RAISE: case AudioManager.ADJUST_RAISE:
increaseDeviceVolume(); increaseDeviceVolume();
...@@ -879,7 +1034,7 @@ import java.util.List; ...@@ -879,7 +1034,7 @@ import java.util.List;
setDeviceMuted(false); setDeviceMuted(false);
break; break;
case AudioManager.ADJUST_TOGGLE_MUTE: case AudioManager.ADJUST_TOGGLE_MUTE:
setDeviceMuted(!isDeviceMuted()); setDeviceMuted(!isDeviceMutedWithCommandCheck());
break; break;
default: default:
Log.w( Log.w(
...@@ -898,6 +1053,9 @@ import java.util.List; ...@@ -898,6 +1053,9 @@ import java.util.List;
* <p>This excludes window uid and period uid that wouldn't be preserved when bundling. * <p>This excludes window uid and period uid that wouldn't be preserved when bundling.
*/ */
public PositionInfo createPositionInfoForBundling() { public PositionInfo createPositionInfoForBundling() {
if (!isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) {
return SessionPositionInfo.DEFAULT_POSITION_INFO;
}
return new PositionInfo( return new PositionInfo(
/* windowUid= */ null, /* windowUid= */ null,
getCurrentMediaItemIndex(), getCurrentMediaItemIndex(),
...@@ -916,6 +1074,9 @@ import java.util.List; ...@@ -916,6 +1074,9 @@ import java.util.List;
* <p>This excludes window uid and period uid that wouldn't be preserved when bundling. * <p>This excludes window uid and period uid that wouldn't be preserved when bundling.
*/ */
public SessionPositionInfo createSessionPositionInfoForBundling() { public SessionPositionInfo createSessionPositionInfoForBundling() {
if (!isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)) {
return SessionPositionInfo.DEFAULT;
}
return new SessionPositionInfo( return new SessionPositionInfo(
createPositionInfoForBundling(), createPositionInfoForBundling(),
isPlayingAd(), isPlayingAd(),
...@@ -941,25 +1102,25 @@ import java.util.List; ...@@ -941,25 +1102,25 @@ import java.util.List;
getRepeatMode(), getRepeatMode(),
getShuffleModeEnabled(), getShuffleModeEnabled(),
getVideoSize(), getVideoSize(),
getCurrentTimeline(), getCurrentTimelineWithCommandCheck(),
getPlaylistMetadata(), getPlaylistMetadataWithCommandCheck(),
getVolume(), getVolumeWithCommandCheck(),
getAudioAttributes(), getAudioAttributesWithCommandCheck(),
getCurrentCues(), getCurrentCuesWithCommandCheck(),
getDeviceInfo(), getDeviceInfo(),
getDeviceVolume(), getDeviceVolumeWithCommandCheck(),
isDeviceMuted(), isDeviceMutedWithCommandCheck(),
getPlayWhenReady(), getPlayWhenReady(),
PlayerInfo.PLAY_WHEN_READY_CHANGE_REASON_DEFAULT, PlayerInfo.PLAY_WHEN_READY_CHANGE_REASON_DEFAULT,
getPlaybackSuppressionReason(), getPlaybackSuppressionReason(),
getPlaybackState(), getPlaybackState(),
isPlaying(), isPlaying(),
isLoading(), isLoading(),
getMediaMetadata(), getMediaMetadataWithCommandCheck(),
getSeekBackIncrement(), getSeekBackIncrement(),
getSeekForwardIncrement(), getSeekForwardIncrement(),
getMaxSeekToPreviousPosition(), getMaxSeekToPreviousPosition(),
getCurrentTracks(), getCurrentTracksWithCommandCheck(),
getTrackSelectionParameters()); getTrackSelectionParameters());
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package androidx.media3.session; package androidx.media3.session;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.os.Looper; import android.os.Looper;
...@@ -42,6 +43,7 @@ public class PlayerWrapperTest { ...@@ -42,6 +43,7 @@ public class PlayerWrapperTest {
@Before @Before
public void setUp() { public void setUp() {
playerWrapper = new PlayerWrapper(player); playerWrapper = new PlayerWrapper(player);
when(player.isCommandAvailable(anyInt())).thenReturn(true);
when(player.getApplicationLooper()).thenReturn(Looper.myLooper()); when(player.getApplicationLooper()).thenReturn(Looper.myLooper());
} }
......
...@@ -18,7 +18,9 @@ package androidx.media3.session; ...@@ -18,7 +18,9 @@ package androidx.media3.session;
import static androidx.media.MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER; import static androidx.media.MediaSessionManager.RemoteUserInfo.LEGACY_CONTROLLER;
import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE;
import static androidx.media3.common.Player.COMMAND_PREPARE; 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_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_ERROR_INVALID_STATE;
import static androidx.media3.session.SessionResult.RESULT_SUCCESS; import static androidx.media3.session.SessionResult.RESULT_SUCCESS;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME; import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
...@@ -204,7 +206,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -204,7 +206,8 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
} }
@Test @Test
public void play() throws Exception { public void play_whileReady_callsPlay() throws Exception {
player.playbackState = STATE_READY;
session = session =
new MediaSession.Builder(context, player) new MediaSession.Builder(context, player)
.setId("play") .setId("play")
...@@ -217,6 +220,92 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -217,6 +220,92 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
controller.getTransportControls().play(); controller.getTransportControls().play();
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS); 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 @Test
...@@ -428,7 +517,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -428,7 +517,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
} }
@Test @Test
public void skipToPrevious() throws Exception { public void skipToPrevious_withAllCommandsAvailable_callsSeekToPrevious() throws Exception {
session = session =
new MediaSession.Builder(context, player) new MediaSession.Builder(context, player)
.setId("skipToPrevious") .setId("skipToPrevious")
...@@ -444,7 +533,29 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -444,7 +533,29 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
} }
@Test @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 = session =
new MediaSession.Builder(context, player) new MediaSession.Builder(context, player)
.setId("skipToNext") .setId("skipToNext")
...@@ -460,6 +571,25 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -460,6 +571,25 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
} }
@Test @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 { public void skipToQueueItem() throws Exception {
session = session =
new MediaSession.Builder(context, player) new MediaSession.Builder(context, player)
...@@ -1050,6 +1180,101 @@ public class MediaSessionCallbackWithMediaControllerCompatTest { ...@@ -1050,6 +1180,101 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
} }
@Test @Test
public void prepareFromMediaUri_withoutAvailablePrepareCommand_justCallsSetMediaItems()
throws Exception {
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
MediaSession.Callback callback =
new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
return Futures.immediateFuture(ImmutableList.of(resolvedMediaItem));
}
};
player.commands =
new Player.Commands.Builder().addAllCommands().remove(COMMAND_PREPARE).build();
session =
new MediaSession.Builder(context, player)
.setId("prepareFromMediaUri")
.setCallback(callback)
.build();
controller =
new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().prepareFromUri(Uri.parse("foo://bar"), Bundle.EMPTY);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
}
@Test
public void playFromMediaUri_withoutAvailablePrepareCommand_justCallsSetMediaItemsAndPlay()
throws Exception {
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
MediaSession.Callback callback =
new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
return Futures.immediateFuture(ImmutableList.of(resolvedMediaItem));
}
};
player.commands =
new Player.Commands.Builder().addAllCommands().remove(COMMAND_PREPARE).build();
session =
new MediaSession.Builder(context, player)
.setId("prepareFromMediaUri")
.setCallback(callback)
.build();
controller =
new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().playFromUri(Uri.parse("foo://bar"), Bundle.EMPTY);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
}
@Test
public void playFromMediaUri_withoutAvailablePrepareAndPlayCommand_justCallsSetMediaItems()
throws Exception {
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
MediaSession.Callback callback =
new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
return Futures.immediateFuture(ImmutableList.of(resolvedMediaItem));
}
};
player.commands =
new Player.Commands.Builder()
.addAllCommands()
.removeAll(COMMAND_PREPARE, COMMAND_PLAY_PAUSE)
.build();
session =
new MediaSession.Builder(context, player)
.setId("prepareFromMediaUri")
.setCallback(callback)
.build();
controller =
new RemoteMediaControllerCompat(
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
controller.getTransportControls().playFromUri(Uri.parse("foo://bar"), Bundle.EMPTY);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PREPARE)).isFalse();
assertThat(player.hasMethodBeenCalled(MockPlayer.METHOD_PLAY)).isFalse();
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
}
@Test
public void setRating() throws Exception { public void setRating() throws Exception {
int ratingType = RatingCompat.RATING_5_STARS; int ratingType = RatingCompat.RATING_5_STARS;
float ratingValue = 3.5f; float ratingValue = 3.5f;
......
...@@ -220,7 +220,7 @@ public class MediaSessionKeyEventTest { ...@@ -220,7 +220,7 @@ public class MediaSessionKeyEventTest {
dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false); dispatchMediaKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_DEFAULT_POSITION, TIMEOUT_MS);
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
} }
......
...@@ -38,12 +38,19 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; ...@@ -38,12 +38,19 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.DeviceInfo;
import androidx.media3.common.ForwardingPlayer;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata; import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.Rating; import androidx.media3.common.Rating;
import androidx.media3.common.StarRating; import androidx.media3.common.StarRating;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.Tracks;
import androidx.media3.common.text.CueGroup;
import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.media3.test.session.common.MainLooperTestRule; import androidx.media3.test.session.common.MainLooperTestRule;
...@@ -262,6 +269,168 @@ public class MediaSessionPermissionTest { ...@@ -262,6 +269,168 @@ public class MediaSessionPermissionTest {
TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT)); TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT));
} }
@Test
public void setPlayer_withoutAvailableCommands_doesNotCallProtectedPlayerGetters()
throws Exception {
MockPlayer mockPlayer =
new MockPlayer.Builder()
.setApplicationLooper(threadTestRule.getHandler().getLooper())
.build();
// Set remote device info to ensure we also cover the volume provider compat setup.
mockPlayer.deviceInfo =
new DeviceInfo(DeviceInfo.PLAYBACK_TYPE_REMOTE, /* minVolume= */ 0, /* maxVolume= */ 100);
Player player =
new ForwardingPlayer(mockPlayer) {
@Override
public boolean isCommandAvailable(int command) {
return false;
}
@Override
public Tracks getCurrentTracks() {
throw new UnsupportedOperationException();
}
@Override
public MediaMetadata getMediaMetadata() {
throw new UnsupportedOperationException();
}
@Override
public MediaMetadata getPlaylistMetadata() {
throw new UnsupportedOperationException();
}
@Override
public Timeline getCurrentTimeline() {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentPeriodIndex() {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentMediaItemIndex() {
throw new UnsupportedOperationException();
}
@Override
public int getNextMediaItemIndex() {
throw new UnsupportedOperationException();
}
@Override
public int getPreviousMediaItemIndex() {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public MediaItem getCurrentMediaItem() {
throw new UnsupportedOperationException();
}
@Override
public long getDuration() {
throw new UnsupportedOperationException();
}
@Override
public long getCurrentPosition() {
throw new UnsupportedOperationException();
}
@Override
public long getBufferedPosition() {
throw new UnsupportedOperationException();
}
@Override
public long getTotalBufferedDuration() {
throw new UnsupportedOperationException();
}
@Override
public boolean isCurrentMediaItemDynamic() {
throw new UnsupportedOperationException();
}
@Override
public boolean isCurrentMediaItemLive() {
throw new UnsupportedOperationException();
}
@Override
public boolean isPlayingAd() {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentAdGroupIndex() {
throw new UnsupportedOperationException();
}
@Override
public int getCurrentAdIndexInAdGroup() {
throw new UnsupportedOperationException();
}
@Override
public long getContentDuration() {
throw new UnsupportedOperationException();
}
@Override
public long getContentPosition() {
throw new UnsupportedOperationException();
}
@Override
public long getContentBufferedPosition() {
throw new UnsupportedOperationException();
}
@Override
public AudioAttributes getAudioAttributes() {
throw new UnsupportedOperationException();
}
@Override
public CueGroup getCurrentCues() {
throw new UnsupportedOperationException();
}
@Override
public int getDeviceVolume() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDeviceMuted() {
throw new UnsupportedOperationException();
}
};
MediaSession session = new MediaSession.Builder(context, player).setId(SESSION_ID).build();
MediaController controller =
new MediaController.Builder(context, session.getToken())
.setApplicationLooper(threadTestRule.getHandler().getLooper())
.buildAsync()
.get();
// Test passes if none of the protected player getters have been called.
threadTestRule
.getHandler()
.postAndSync(
() -> {
controller.release();
session.release();
player.release();
});
}
private ControllerInfo getTestControllerInfo() { private ControllerInfo getTestControllerInfo() {
List<ControllerInfo> controllers = session.getConnectedControllers(); List<ControllerInfo> controllers = session.getConnectedControllers();
assertThat(controllers).isNotNull(); assertThat(controllers).isNotNull();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment