Commit bb11e028 by michaelkatz Committed by christosts

Add onSetMediaItems listener with access to start index and position

Added onSetMediaItems callback listener to allow the session to modify/set MediaItem list, starting index and position before call to Player.setMediaItem(s).

Added conditional check in MediaSessionStub.setMediaItem methods to only call player.setMediaItem rather than setMediaItems if player does not support COMMAND_CHANGE_MEDIA_ITEMS

PiperOrigin-RevId: 503427927
parent e690802e
...@@ -52,6 +52,9 @@ Release notes ...@@ -52,6 +52,9 @@ Release notes
* Add the media session as an argument of `getMediaButtons()` of the * Add the media session as an argument of `getMediaButtons()` of the
`DefaultMediaNotificationProvider` and use immutable lists for clarity `DefaultMediaNotificationProvider` and use immutable lists for clarity
([#216](https://github.com/androidx/media/issues/216)). ([#216](https://github.com/androidx/media/issues/216)).
* Add `onSetMediaItems` callback listener to provide means to modify/set
`MediaItem` list, starting index and position by session before setting
onto Player ([#156](https://github.com/androidx/media/issues/156)).
* Metadata: * Metadata:
* Parse multiple null-separated values from ID3 frames, as permitted by * Parse multiple null-separated values from ID3 frames, as permitted by
ID3 v2.4. ID3 v2.4.
......
...@@ -70,6 +70,7 @@ import androidx.media3.common.util.Log; ...@@ -70,6 +70,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.session.MediaSession.ControllerCb; import androidx.media3.session.MediaSession.ControllerCb;
import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.session.MediaSession.MediaItemsWithStartPosition;
import androidx.media3.session.SequencedFutureManager.SequencedFuture; import androidx.media3.session.SequencedFutureManager.SequencedFuture;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
...@@ -525,6 +526,13 @@ import org.checkerframework.checker.initialization.qual.Initialized; ...@@ -525,6 +526,13 @@ import org.checkerframework.checker.initialization.qual.Initialized;
"onAddMediaItems must return a non-null future"); "onAddMediaItems must return a non-null future");
} }
protected ListenableFuture<MediaItemsWithStartPosition> onSetMediaItemsOnHandler(
ControllerInfo controller, List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
return checkNotNull(
callback.onSetMediaItems(instance, controller, mediaItems, startIndex, startPositionMs),
"onSetMediaItems must return a non-null future");
}
protected boolean isReleased() { protected boolean isReleased() {
synchronized (lock) { synchronized (lock) {
return closed; return closed;
......
...@@ -85,6 +85,7 @@ import androidx.media3.common.util.Log; ...@@ -85,6 +85,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.session.MediaSession.ControllerCb; import androidx.media3.session.MediaSession.ControllerCb;
import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.session.MediaSession.MediaItemsWithStartPosition;
import androidx.media3.session.SessionCommand.CommandCode; import androidx.media3.session.SessionCommand.CommandCode;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
...@@ -711,18 +712,26 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -711,18 +712,26 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
dispatchSessionTaskWithPlayerCommand( dispatchSessionTaskWithPlayerCommand(
COMMAND_SET_MEDIA_ITEM, COMMAND_SET_MEDIA_ITEM,
controller -> { controller -> {
ListenableFuture<List<MediaItem>> mediaItemsFuture = ListenableFuture<MediaItemsWithStartPosition> mediaItemsFuture =
sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)); sessionImpl.onSetMediaItemsOnHandler(
controller, ImmutableList.of(mediaItem), C.INDEX_UNSET, C.TIME_UNSET);
Futures.addCallback( Futures.addCallback(
mediaItemsFuture, mediaItemsFuture,
new FutureCallback<List<MediaItem>>() { new FutureCallback<MediaItemsWithStartPosition>() {
@Override @Override
public void onSuccess(List<MediaItem> mediaItems) { public void onSuccess(MediaItemsWithStartPosition mediaItemsWithStartPosition) {
postOrRun( postOrRun(
sessionImpl.getApplicationHandler(), sessionImpl.getApplicationHandler(),
() -> { () -> {
PlayerWrapper player = sessionImpl.getPlayerWrapper(); PlayerWrapper player = sessionImpl.getPlayerWrapper();
player.setMediaItems(mediaItems); if (mediaItemsWithStartPosition.startIndex == C.INDEX_UNSET
&& mediaItemsWithStartPosition.startPositionMs == C.TIME_UNSET) {
MediaUtils.setMediaItemsWithDefaultStartIndexAndPosition(
player, mediaItemsWithStartPosition);
} else {
MediaUtils.setMediaItemsWithSpecifiedStartIndexAndPosition(
player, mediaItemsWithStartPosition);
}
@Player.State int playbackState = player.getPlaybackState(); @Player.State int playbackState = player.getPlaybackState();
if (playbackState == Player.STATE_IDLE) { if (playbackState == Player.STATE_IDLE) {
player.prepareIfCommandAvailable(); player.prepareIfCommandAvailable();
......
...@@ -1368,5 +1368,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1368,5 +1368,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
: Util.constrainValue((int) ((bufferedPositionMs * 100) / durationMs), 0, 100); : Util.constrainValue((int) ((bufferedPositionMs * 100) / durationMs), 0, 100);
} }
public static void setMediaItemsWithDefaultStartIndexAndPosition(
PlayerWrapper player, MediaSession.MediaItemsWithStartPosition mediaItemsWithStartPosition) {
if (player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)) {
player.setMediaItems(mediaItemsWithStartPosition.mediaItems, /* resetPosition= */ true);
} else if (!mediaItemsWithStartPosition.mediaItems.isEmpty()) {
player.setMediaItem(mediaItemsWithStartPosition.mediaItems.get(0), /* resetPosition= */ true);
} else {
player.clearMediaItems();
}
}
public static void setMediaItemsWithSpecifiedStartIndexAndPosition(
PlayerWrapper player, MediaSession.MediaItemsWithStartPosition mediaItemsWithStartPosition) {
if (player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)) {
player.setMediaItems(
mediaItemsWithStartPosition.mediaItems,
mediaItemsWithStartPosition.startIndex,
mediaItemsWithStartPosition.startPositionMs);
} else if (!mediaItemsWithStartPosition.mediaItems.isEmpty()) {
player.setMediaItem(
mediaItemsWithStartPosition.mediaItems.get(0),
mediaItemsWithStartPosition.startPositionMs);
} else {
player.clearMediaItems();
}
}
private MediaUtils() {} private MediaUtils() {}
} }
...@@ -300,7 +300,7 @@ public class MediaSessionPlayerTest { ...@@ -300,7 +300,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems).containsExactly(item); assertThat(player.mediaItems).containsExactly(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
...@@ -316,7 +316,7 @@ public class MediaSessionPlayerTest { ...@@ -316,7 +316,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems).containsExactly(item); assertThat(player.mediaItems).containsExactly(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
...@@ -332,7 +332,7 @@ public class MediaSessionPlayerTest { ...@@ -332,7 +332,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItem(item); controller.setMediaItem(item);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems).containsExactly(item); assertThat(player.mediaItems).containsExactly(item);
assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition); assertThat(player.resetPosition).isEqualTo(resetPosition);
...@@ -344,9 +344,9 @@ public class MediaSessionPlayerTest { ...@@ -344,9 +344,9 @@ public class MediaSessionPlayerTest {
controller.setMediaItems(items); controller.setMediaItems(items);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems).isEqualTo(items); assertThat(player.mediaItems).isEqualTo(items);
assertThat(player.resetPosition).isFalse(); assertThat(player.resetPosition).isTrue();
} }
@Test @Test
...@@ -382,7 +382,7 @@ public class MediaSessionPlayerTest { ...@@ -382,7 +382,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItems(list); controller.setMediaItems(list);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems.size()).isEqualTo(listSize); assertThat(player.mediaItems.size()).isEqualTo(listSize);
for (int i = 0; i < listSize; i++) { for (int i = 0; i < listSize; i++) {
assertThat(player.mediaItems.get(i).mediaId).isEqualTo(list.get(i).mediaId); assertThat(player.mediaItems.get(i).mediaId).isEqualTo(list.get(i).mediaId);
...@@ -395,7 +395,7 @@ public class MediaSessionPlayerTest { ...@@ -395,7 +395,7 @@ public class MediaSessionPlayerTest {
// Make client app to generate a long list, and call setMediaItems() with it. // Make client app to generate a long list, and call setMediaItems() with it.
controller.createAndSetFakeMediaItems(listSize); controller.createAndSetFakeMediaItems(listSize);
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS);
assertThat(player.mediaItems).isNotNull(); assertThat(player.mediaItems).isNotNull();
assertThat(player.mediaItems.size()).isEqualTo(listSize); assertThat(player.mediaItems.size()).isEqualTo(listSize);
for (int i = 0; i < listSize; i++) { for (int i = 0; i < listSize; i++) {
...@@ -824,7 +824,7 @@ public class MediaSessionPlayerTest { ...@@ -824,7 +824,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItemsPreparePlayAddItemsSeek(initialItems, addedItems, /* seekIndex= */ 3); controller.setMediaItemsPreparePlayAddItemsSeek(initialItems, addedItems, /* seekIndex= */ 3);
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
boolean setMediaItemsCalledBeforePrepare = boolean setMediaItemsCalledBeforePrepare =
player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS); player.hasMethodBeenCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION);
player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS); player.awaitMethodCalled(MockPlayer.METHOD_SEEK_TO_WITH_MEDIA_ITEM_INDEX, TIMEOUT_MS);
boolean addMediaItemsCalledBeforeSeek = boolean addMediaItemsCalledBeforeSeek =
player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS); player.hasMethodBeenCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS);
......
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