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
* Add the media session as an argument of `getMediaButtons()` of the
`DefaultMediaNotificationProvider` and use immutable lists for clarity
([#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:
* Parse multiple null-separated values from ID3 frames, as permitted by
ID3 v2.4.
......
......@@ -70,6 +70,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.session.MediaSession.ControllerCb;
import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.session.MediaSession.MediaItemsWithStartPosition;
import androidx.media3.session.SequencedFutureManager.SequencedFuture;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
......@@ -525,6 +526,13 @@ import org.checkerframework.checker.initialization.qual.Initialized;
"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() {
synchronized (lock) {
return closed;
......
......@@ -85,6 +85,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;
import androidx.media3.session.MediaSession.ControllerCb;
import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.session.MediaSession.MediaItemsWithStartPosition;
import androidx.media3.session.SessionCommand.CommandCode;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
......@@ -711,18 +712,26 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
dispatchSessionTaskWithPlayerCommand(
COMMAND_SET_MEDIA_ITEM,
controller -> {
ListenableFuture<List<MediaItem>> mediaItemsFuture =
sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem));
ListenableFuture<MediaItemsWithStartPosition> mediaItemsFuture =
sessionImpl.onSetMediaItemsOnHandler(
controller, ImmutableList.of(mediaItem), C.INDEX_UNSET, C.TIME_UNSET);
Futures.addCallback(
mediaItemsFuture,
new FutureCallback<List<MediaItem>>() {
new FutureCallback<MediaItemsWithStartPosition>() {
@Override
public void onSuccess(List<MediaItem> mediaItems) {
public void onSuccess(MediaItemsWithStartPosition mediaItemsWithStartPosition) {
postOrRun(
sessionImpl.getApplicationHandler(),
() -> {
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();
if (playbackState == Player.STATE_IDLE) {
player.prepareIfCommandAvailable();
......
......@@ -1368,5 +1368,32 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
: 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() {}
}
......@@ -300,7 +300,7 @@ public class MediaSessionPlayerTest {
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.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition);
......@@ -316,7 +316,7 @@ public class MediaSessionPlayerTest {
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.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition);
......@@ -332,7 +332,7 @@ public class MediaSessionPlayerTest {
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.startPositionMs).isEqualTo(startPositionMs);
assertThat(player.resetPosition).isEqualTo(resetPosition);
......@@ -344,9 +344,9 @@ public class MediaSessionPlayerTest {
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.resetPosition).isFalse();
assertThat(player.resetPosition).isTrue();
}
@Test
......@@ -382,7 +382,7 @@ public class MediaSessionPlayerTest {
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);
for (int i = 0; i < listSize; i++) {
assertThat(player.mediaItems.get(i).mediaId).isEqualTo(list.get(i).mediaId);
......@@ -395,7 +395,7 @@ public class MediaSessionPlayerTest {
// Make client app to generate a long list, and call setMediaItems() with it.
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.size()).isEqualTo(listSize);
for (int i = 0; i < listSize; i++) {
......@@ -824,7 +824,7 @@ public class MediaSessionPlayerTest {
controller.setMediaItemsPreparePlayAddItemsSeek(initialItems, addedItems, /* seekIndex= */ 3);
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
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);
boolean addMediaItemsCalledBeforeSeek =
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