diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4f26fe013f..5efdf34cef 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -129,6 +129,9 @@ `MediaLibrarySession.MediaLibrarySessionCallback` to `MediaLibrarySession.Callback` and `MediaSession.Builder.setSessionCallback` to `setCallback`. + * Replace `MediaSession.MediaItemFiler` with + `MediaSession.Callback.onAddMediaItems` to allow asynchronous resolution + of requests. * Data sources: * Rename `DummyDataSource` to `PlaceHolderDataSource`. * Workaround OkHttp interrupt handling. diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt index 39bca5a8f7..25c012275a 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt @@ -202,6 +202,16 @@ class PlaybackService : MediaLibraryService() { } } + override fun onAddMediaItems( + mediaSession: MediaSession, + controller: MediaSession.ControllerInfo, + mediaItems: List + ): ListenableFuture> { + val updatedMediaItems: List = + mediaItems.map { mediaItem -> MediaItemTree.getItem(mediaItem.mediaId) ?: mediaItem } + return Futures.immediateFuture(updatedMediaItems) + } + private fun setMediaItemFromSearchQuery(query: String) { // Only accept query with pattern "play [Title]" or "[Title]" // Where [Title]: must be exactly matched @@ -236,7 +246,6 @@ class PlaybackService : MediaLibraryService() { mediaLibrarySession = MediaLibrarySession.Builder(this, player, librarySessionCallback) - .setMediaItemFiller(CustomMediaItemFiller()) .setSessionActivity(sessionActivityPendingIntent) .build() if (!customLayout.isEmpty()) { @@ -262,14 +271,4 @@ class PlaybackService : MediaLibraryService() { private fun ignoreFuture(customLayout: ListenableFuture) { /* Do nothing. */ } - - private class CustomMediaItemFiller : MediaSession.MediaItemFiller { - override fun fillInLocalConfiguration( - session: MediaSession, - controller: ControllerInfo, - mediaItem: MediaItem - ): MediaItem { - return MediaItemTree.getItem(mediaItem.mediaId) ?: mediaItem - } - } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java index bc1e1b6adb..8533019b1d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java @@ -406,17 +406,6 @@ public abstract class MediaLibraryService extends MediaSessionService { return super.setId(id); } - /** - * Sets the logic used to fill in the fields of a {@link MediaItem}. - * - * @param mediaItemFiller The filler. - * @return The builder to allow chaining. - */ - @Override - public Builder setMediaItemFiller(MediaItemFiller mediaItemFiller) { - return super.setMediaItemFiller(mediaItemFiller); - } - /** * Sets an extra {@link Bundle} for the {@link MediaLibrarySession}. The {@link * MediaLibrarySession#getToken()} session token} will have the {@link @@ -439,8 +428,7 @@ public abstract class MediaLibraryService extends MediaSessionService { */ @Override public MediaLibrarySession build() { - return new MediaLibrarySession( - context, id, player, sessionActivity, callback, mediaItemFiller, extras); + return new MediaLibrarySession(context, id, player, sessionActivity, callback, extras); } } @@ -450,9 +438,8 @@ public abstract class MediaLibraryService extends MediaSessionService { Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - MediaItemFiller mediaItemFiller, Bundle tokenExtras) { - super(context, id, player, sessionActivity, callback, mediaItemFiller, tokenExtras); + super(context, id, player, sessionActivity, callback, tokenExtras); } @Override @@ -462,17 +449,9 @@ public abstract class MediaLibraryService extends MediaSessionService { Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - MediaItemFiller mediaItemFiller, Bundle tokenExtras) { return new MediaLibrarySessionImpl( - this, - context, - id, - player, - sessionActivity, - (Callback) callback, - mediaItemFiller, - tokenExtras); + this, context, id, player, sessionActivity, (Callback) callback, tokenExtras); } @Override diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index 46a7651037..8dff9a43e1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -63,9 +63,8 @@ import java.util.concurrent.Future; Player player, @Nullable PendingIntent sessionActivity, MediaLibrarySession.Callback callback, - MediaSession.MediaItemFiller mediaItemFiller, Bundle tokenExtras) { - super(instance, context, id, player, sessionActivity, callback, mediaItemFiller, tokenExtras); + super(instance, context, id, player, sessionActivity, callback, tokenExtras); this.instance = instance; this.callback = callback; subscriptions = new ArrayMap<>(); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index cc91b1ae3e..94a3d331ea 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -293,18 +293,6 @@ public class MediaSession { return super.setCallback(callback); } - /** - * Sets the logic used to fill in the fields of a {@link MediaItem} from {@link - * MediaController}. - * - * @param mediaItemFiller The filler. - * @return The builder to allow chaining. - */ - @Override - public Builder setMediaItemFiller(MediaItemFiller mediaItemFiller) { - return super.setMediaItemFiller(mediaItemFiller); - } - /** * Sets an extra {@link Bundle} for the {@link MediaSession}. The {@link * MediaSession#getToken()} session token} will have the {@link SessionToken#getExtras() @@ -327,8 +315,7 @@ public class MediaSession { */ @Override public MediaSession build() { - return new MediaSession( - context, id, player, sessionActivity, callback, mediaItemFiller, extras); + return new MediaSession(context, id, player, sessionActivity, callback, extras); } } @@ -484,7 +471,6 @@ public class MediaSession { Player player, @Nullable PendingIntent sessionActivity, Callback callback, - MediaItemFiller mediaItemFiller, Bundle tokenExtras) { synchronized (STATIC_LOCK) { if (SESSION_ID_TO_SESSION_MAP.containsKey(id)) { @@ -492,7 +478,7 @@ public class MediaSession { } SESSION_ID_TO_SESSION_MAP.put(id, this); } - impl = createImpl(context, id, player, sessionActivity, callback, mediaItemFiller, tokenExtras); + impl = createImpl(context, id, player, sessionActivity, callback, tokenExtras); } /* package */ MediaSessionImpl createImpl( @@ -501,10 +487,8 @@ public class MediaSession { Player player, @Nullable PendingIntent sessionActivity, Callback callback, - MediaItemFiller mediaItemFiller, Bundle tokenExtras) { - return new MediaSessionImpl( - this, context, id, player, sessionActivity, callback, mediaItemFiller, tokenExtras); + return new MediaSessionImpl(this, context, id, player, sessionActivity, callback, tokenExtras); } /* package */ MediaSessionImpl getImpl() { @@ -1041,23 +1025,29 @@ public class MediaSession { Bundle args) { return Futures.immediateFuture(new SessionResult(RESULT_ERROR_NOT_SUPPORTED)); } - } - - /** An object which fills in the fields of a {@link MediaItem} from {@link MediaController}. */ - public interface MediaItemFiller { /** - * Called to fill in the {@link MediaItem#localConfiguration} of the media item from - * controllers. + * Called when a controller requested to add new {@linkplain MediaItem media items} to the + * playlist. * - * @param session The session for this event. + *

Note that the requested {@linkplain MediaItem media items} don't have a {@link + * MediaItem.LocalConfiguration} (for example, a URI) and need to be updated to make them + * playable by the underlying {@link Player}. Typically, this implementation should be able to + * identify the correct item by its {@link MediaItem#mediaId} and/or the {@link + * MediaItem#requestMetadata}. + * + *

Return a {@link ListenableFuture} with the resolved {@link MediaItem media items}. You can + * also return the items directly by using Guava's {@link Futures#immediateFuture(Object)}. + * + * @param mediaSession The session for this event. * @param controller The controller information. - * @param mediaItem The media item whose local configuration will be filled in. - * @return A media item with filled local configuration. + * @param mediaItems The list of requested {@link MediaItem media items}. + * @return A {@link ListenableFuture} for the list of resolved {@link MediaItem media items} + * that are playable by the underlying {@link Player}. */ - default MediaItem fillInLocalConfiguration( - MediaSession session, MediaSession.ControllerInfo controller, MediaItem mediaItem) { - return mediaItem; + default ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + return Futures.immediateFailedFuture(new UnsupportedOperationException()); } } @@ -1230,7 +1220,6 @@ public class MediaSession { /* package */ final Player player; /* package */ String id; /* package */ C callback; - /* package */ MediaItemFiller mediaItemFiller; /* package */ @Nullable PendingIntent sessionActivity; /* package */ Bundle extras; @@ -1240,7 +1229,6 @@ public class MediaSession { checkArgument(player.canAdvertiseSession()); id = ""; this.callback = callback; - this.mediaItemFiller = new MediaItemFiller() {}; extras = Bundle.EMPTY; } @@ -1262,12 +1250,6 @@ public class MediaSession { return (U) this; } - @SuppressWarnings("unchecked") - /* package */ U setMediaItemFiller(MediaItemFiller mediaItemFiller) { - this.mediaItemFiller = checkNotNull(mediaItemFiller); - return (U) this; - } - @SuppressWarnings("unchecked") public U setExtras(Bundle extras) { this.extras = new Bundle(checkNotNull(extras)); 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 9bfe995ef6..d82e1d54d1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -67,7 +67,6 @@ 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.MediaItemFiller; import androidx.media3.session.SequencedFutureManager.SequencedFuture; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; @@ -104,12 +103,8 @@ import org.checkerframework.checker.initialization.qual.Initialized; protected final Object lock = new Object(); private final Uri sessionUri; - private final PlayerInfoChangedHandler onPlayerInfoChangedHandler; - private final MediaSession.Callback callback; - private final MediaItemFiller mediaItemFiller; - private final Context context; private final MediaSessionStub sessionStub; private final MediaSessionLegacyStub sessionLegacyStub; @@ -143,7 +138,6 @@ import org.checkerframework.checker.initialization.qual.Initialized; Player player, @Nullable PendingIntent sessionActivity, MediaSession.Callback callback, - MediaItemFiller mediaItemFiller, Bundle tokenExtras) { this.context = context; this.instance = instance; @@ -157,7 +151,6 @@ import org.checkerframework.checker.initialization.qual.Initialized; applicationHandler = new Handler(player.getApplicationLooper()); this.callback = callback; - this.mediaItemFiller = mediaItemFiller; playerInfo = PlayerInfo.DEFAULT; onPlayerInfoChangedHandler = new PlayerInfoChangedHandler(player.getApplicationLooper()); @@ -495,9 +488,11 @@ import org.checkerframework.checker.initialization.qual.Initialized; return applicationHandler; } - protected MediaItem fillInLocalConfiguration( - MediaSession.ControllerInfo controller, MediaItem mediaItem) { - return mediaItemFiller.fillInLocalConfiguration(instance, controller, mediaItem); + protected ListenableFuture> onAddMediaItemsOnHandler( + ControllerInfo controller, List mediaItems) { + return checkNotNull( + callback.onAddMediaItems(instance, controller, mediaItems), + "onAddMediaItems must return a non-null future"); } protected boolean isReleased() { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java index c98404ca92..743af2c08e 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -147,6 +147,36 @@ import java.util.concurrent.ExecutionException; MoreExecutors.directExecutor()); } + private static void handleMediaItemsWhenReady( + K sessionImpl, + ControllerInfo controller, + int seq, + ListenableFuture> future, + MediaItemPlayerTask mediaItemPlayerTask) { + future.addListener( + () -> { + SessionResult result; + try { + List mediaItems = + checkNotNull(future.get(), "MediaItem list must not be null"); + postOrRun( + sessionImpl.getApplicationHandler(), + () -> mediaItemPlayerTask.run(sessionImpl.getPlayerWrapper(), mediaItems)); + result = new SessionResult(SessionResult.RESULT_SUCCESS); + } catch (CancellationException unused) { + result = new SessionResult(SessionResult.RESULT_INFO_SKIPPED); + } catch (ExecutionException | InterruptedException exception) { + result = + new SessionResult( + exception.getCause() instanceof UnsupportedOperationException + ? SessionResult.RESULT_ERROR_NOT_SUPPORTED + : SessionResult.RESULT_ERROR_UNKNOWN); + } + sendSessionResult(sessionImpl, controller, seq, result); + }, + MoreExecutors.directExecutor()); + } + private static void sendLibraryResult( ControllerInfo controller, int seq, LibraryResult result) { try { @@ -816,13 +846,11 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - sessionImpl.getPlayerWrapper().setMediaItem(mediaItemWithPlaybackProperties); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, controller, sequence, future, Player::setMediaItems)); } @Override @@ -845,15 +873,16 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - sessionImpl - .getPlayerWrapper() - .setMediaItem(mediaItemWithPlaybackProperties, startPositionMs); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, mediaItems) -> + player.setMediaItems(mediaItems, /* startIndex= */ 0, startPositionMs))); } @Override @@ -876,15 +905,15 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - sessionImpl - .getPlayerWrapper() - .setMediaItem(mediaItemWithPlaybackProperties, resetPosition); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, mediaItems) -> player.setMediaItems(mediaItems, resetPosition))); } @Override @@ -907,20 +936,11 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - ImmutableList.Builder mediaItemWithPlaybackPropertiesListBuilder = - ImmutableList.builder(); - for (MediaItem mediaItem : mediaItemList) { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - mediaItemWithPlaybackPropertiesListBuilder.add(mediaItemWithPlaybackProperties); - } - sessionImpl - .getPlayerWrapper() - .setMediaItems(mediaItemWithPlaybackPropertiesListBuilder.build()); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, mediaItemList), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, controller, sequence, future, Player::setMediaItems)); } @Override @@ -945,20 +965,15 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - ImmutableList.Builder mediaItemWithPlaybackPropertiesListBuilder = - ImmutableList.builder(); - for (MediaItem mediaItem : mediaItemList) { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - mediaItemWithPlaybackPropertiesListBuilder.add(mediaItemWithPlaybackProperties); - } - sessionImpl - .getPlayerWrapper() - .setMediaItems(mediaItemWithPlaybackPropertiesListBuilder.build(), resetPosition); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, mediaItemList), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, mediaItems) -> player.setMediaItems(mediaItems, resetPosition))); } @Override @@ -984,22 +999,16 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - ImmutableList.Builder mediaItemWithPlaybackPropertiesListBuilder = - ImmutableList.builder(); - for (MediaItem mediaItem : mediaItemList) { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - mediaItemWithPlaybackPropertiesListBuilder.add(mediaItemWithPlaybackProperties); - } - - sessionImpl - .getPlayerWrapper() - .setMediaItems( - mediaItemWithPlaybackPropertiesListBuilder.build(), startIndex, startPositionMs); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, mediaItemList), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, mediaItems) -> + player.setMediaItems(mediaItems, startIndex, startPositionMs))); } @Override @@ -1057,13 +1066,11 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - sessionImpl.getPlayerWrapper().addMediaItem(mediaItemWithPlaybackProperties); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, controller, sequence, future, Player::addMediaItems)); } @Override @@ -1083,13 +1090,15 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - sessionImpl.getPlayerWrapper().addMediaItem(index, mediaItemWithPlaybackProperties); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> + sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem)), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, mediaItems) -> player.addMediaItems(index, mediaItems))); } @Override @@ -1111,21 +1120,10 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - ImmutableList.Builder mediaItemsWithPlaybackPropertiesBuilder = - ImmutableList.builder(); - for (MediaItem mediaItem : mediaItems) { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - mediaItemsWithPlaybackPropertiesBuilder.add(mediaItemWithPlaybackProperties); - } - - sessionImpl - .getPlayerWrapper() - .addMediaItems(mediaItemsWithPlaybackPropertiesBuilder.build()); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> sessionImpl.onAddMediaItemsOnHandler(controller, mediaItems), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, controller, sequence, future, Player::addMediaItems)); } @Override @@ -1150,21 +1148,14 @@ import java.util.concurrent.ExecutionException; caller, seq, COMMAND_CHANGE_MEDIA_ITEMS, - (sessionImpl, controller) -> { - ImmutableList.Builder mediaItemsWithPlaybackPropertiesBuilder = - ImmutableList.builder(); - for (MediaItem mediaItem : mediaItems) { - MediaItem mediaItemWithPlaybackProperties = - sessionImpl.fillInLocalConfiguration(controller, mediaItem); - mediaItemsWithPlaybackPropertiesBuilder.add(mediaItemWithPlaybackProperties); - } - - sessionImpl - .getPlayerWrapper() - .addMediaItems(index, mediaItemsWithPlaybackPropertiesBuilder.build()); - return SessionResult.RESULT_SUCCESS; - }, - MediaSessionStub::sendSessionResult); + (sessionImpl, controller) -> sessionImpl.onAddMediaItemsOnHandler(controller, mediaItems), + (sessionImpl, controller, sequence, future) -> + handleMediaItemsWhenReady( + sessionImpl, + controller, + sequence, + future, + (player, items) -> player.addMediaItems(index, items))); } @Override @@ -1709,6 +1700,10 @@ import java.util.concurrent.ExecutionException; void run(K sessionImpl, ControllerInfo controller, int seq, T result); } + private interface MediaItemPlayerTask { + void run(PlayerWrapper player, List mediaItems); + } + /* package */ static final class Controller2Cb implements ControllerCb { private final IMediaController iController; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java index 4175a5318c..b079208c35 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionCallbackTest.java @@ -15,9 +15,11 @@ */ package androidx.media3.session; +import static androidx.media3.session.MediaTestUtils.createMediaItem; import static androidx.media3.session.SessionResult.RESULT_ERROR_INVALID_STATE; import static androidx.media3.session.SessionResult.RESULT_INFO_SKIPPED; import static androidx.media3.session.SessionResult.RESULT_SUCCESS; +import static androidx.media3.test.session.common.CommonConstants.METADATA_MEDIA_URI; import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME; import static androidx.media3.test.session.common.TestUtils.NO_RESPONSE_TIMEOUT_MS; import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS; @@ -39,12 +41,18 @@ import androidx.media3.test.session.common.TestUtils; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -68,6 +76,7 @@ public class MediaSessionCallbackTest { private Context context; private MockPlayer player; + private ListeningExecutorService executorService; @Before public void setUp() { @@ -76,6 +85,14 @@ public class MediaSessionCallbackTest { new MockPlayer.Builder() .setApplicationLooper(threadTestRule.getHandler().getLooper()) .build(); + // Intentionally use an Executor with another thread to test asynchronous workflows involving + // background tasks. + executorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + } + + @After + public void tearDown() { + executorService.shutdownNow(); } @Test @@ -335,168 +352,301 @@ public class MediaSessionCallbackTest { } @Test - public void onFillInPlaybackProperties_setMediaItem() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItem("mediaId"); - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 1); + public void onAddMediaItems_withSetMediaItem() throws Exception { + MediaItem mediaItem = createMediaItem("mediaId"); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.setMediaItem(mediaItem); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactly(mediaItem); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactly(mediaItem); + assertThat(player.mediaItems).containsExactly(updateMediaItemWithLocalConfiguration(mediaItem)); } @Test - public void onFillInPlaybackProperties_setMediaItemWithIndex() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItem("mediaId"); - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 1); + public void onAddMediaItems_withSetMediaItemWithIndex() throws Exception { + MediaItem mediaItem = createMediaItem("mediaId"); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); - controller.setMediaItem(mediaItem, /* startPositionMs= */ 0); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactly(mediaItem); + controller.setMediaItem(mediaItem, /* startPositionMs= */ 1234); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactly(mediaItem); + assertThat(player.mediaItems).containsExactly(updateMediaItemWithLocalConfiguration(mediaItem)); + assertThat(player.startMediaItemIndex).isEqualTo(0); + assertThat(player.startPositionMs).isEqualTo(1234); } @Test - public void onFillInPlaybackProperties_setMediaItemWithResetPosition() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItem("mediaId"); - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 1); + public void onAddMediaItems_withSetMediaItemWithResetPosition() throws Exception { + MediaItem mediaItem = createMediaItem("mediaId"); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.setMediaItem(mediaItem, /* resetPosition= */ true); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactly(mediaItem); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactly(mediaItem); + assertThat(player.mediaItems).containsExactly(updateMediaItemWithLocalConfiguration(mediaItem)); + assertThat(player.resetPosition).isEqualTo(true); } @Test - public void onFillInPlaybackProperties_setMediaItems() throws Exception { + public void onAddMediaItems_withSetMediaItems() throws Exception { List mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); - - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 3); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.setMediaItems(mediaItems); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactlyElementsIn(mediaItems); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder(); + assertThat(player.mediaItems) + .containsExactlyElementsIn(updateMediaItemsWithLocalConfiguration(mediaItems)) + .inOrder(); } @Test - public void onFillInPlaybackProperties_setMediaItemsWithStartPosition() throws Exception { + public void onAddMediaItems_withSetMediaItemsWithStartPosition() throws Exception { List mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); - - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 3); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); - controller.setMediaItems(mediaItems, /* startWindowIndex= */ 0, /* startPositionMs= */ 0); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactlyElementsIn(mediaItems); + controller.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 1234); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_START_INDEX, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder(); + assertThat(player.mediaItems) + .containsExactlyElementsIn(updateMediaItemsWithLocalConfiguration(mediaItems)) + .inOrder(); + assertThat(player.startMediaItemIndex).isEqualTo(1); + assertThat(player.startPositionMs).isEqualTo(1234); } @Test - public void onFillInPlaybackProperties_setMediaItemsWithResetPosition() throws Exception { + public void onAddMediaItems_withSetMediaItemsWithResetPosition() throws Exception { List mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); - - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 3); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.setMediaItems(mediaItems, /* resetPosition= */ true); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactlyElementsIn(mediaItems); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS_WITH_RESET_POSITION, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder(); + assertThat(player.mediaItems) + .containsExactlyElementsIn(updateMediaItemsWithLocalConfiguration(mediaItems)) + .inOrder(); + assertThat(player.resetPosition).isEqualTo(true); } @Test - public void onFillInPlaybackProperties_addMediaItem() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItem("mediaId"); - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 1); + public void onAddMediaItems_withAddMediaItem() throws Exception { + MediaItem mediaItem = createMediaItem("mediaId"); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.addMediaItem(mediaItem); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactly(mediaItem); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactly(mediaItem); + assertThat(player.mediaItems).containsExactly(updateMediaItemWithLocalConfiguration(mediaItem)); } @Test - public void onFillInPlaybackProperties_addMediaItemWithIndex() throws Exception { - MediaItem mediaItem = MediaTestUtils.createMediaItem("mediaId"); - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 1); + public void onAddMediaItems_withAddMediaItemWithIndex() throws Exception { + MediaItem existingItem = createMediaItem("existingItem"); + MediaItem mediaItem = createMediaItem("mediaId"); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); + controller.setMediaItem(existingItem); - controller.addMediaItem(/* index= */ 0, mediaItem); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactly(mediaItem); + controller.addMediaItem(/* index= */ 1, mediaItem); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactly(mediaItem); + assertThat(player.mediaItems) + .containsExactly( + updateMediaItemWithLocalConfiguration(existingItem), + updateMediaItemWithLocalConfiguration(mediaItem)); + assertThat(player.index).isEqualTo(1); } @Test - public void onFillInPlaybackProperties_addMediaItems() throws Exception { + public void onAddMediaItems_withAddMediaItems() throws Exception { List mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); - - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 3); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); controller.addMediaItems(mediaItems); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactlyElementsIn(mediaItems); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder(); + assertThat(player.mediaItems) + .containsExactlyElementsIn(updateMediaItemsWithLocalConfiguration(mediaItems)) + .inOrder(); } @Test - public void onFillInPlaybackProperties_addMediaItemsWithIndex() throws Exception { + public void onAddMediaItems_withAddMediaItemsWithIndex() throws Exception { + MediaItem existingItem = createMediaItem("existingItem"); List mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); - - MockFillInLocalConfigurationCallback callback = - new MockFillInLocalConfigurationCallback(/* latchCount= */ 3); + AtomicReference> requestedMediaItems = new AtomicReference<>(); + MediaSession.Callback callback = + new MediaSession.Callback() { + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, ControllerInfo controller, List mediaItems) { + requestedMediaItems.set(mediaItems); + // Resolve MediaItems asynchronously to test correct threading logic. + return executorService.submit(() -> updateMediaItemsWithLocalConfiguration(mediaItems)); + } + }; MediaSession session = sessionTestRule.ensureReleaseAfterTest( - new MediaSession.Builder(context, player).setMediaItemFiller(callback).build()); + new MediaSession.Builder(context, player).setCallback(callback).build()); RemoteMediaController controller = controllerTestRule.createRemoteController(session.getToken()); + controller.setMediaItem(existingItem); - controller.addMediaItems(/* index= */ 0, mediaItems); - assertThat(callback.latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(callback.mediaItemsFromParam).containsExactlyElementsIn(mediaItems); + controller.addMediaItems(/* index= */ 1, mediaItems); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, TIMEOUT_MS); + + assertThat(requestedMediaItems.get()).containsExactlyElementsIn(mediaItems).inOrder(); + assertThat(player.mediaItems) + .containsExactlyElementsIn( + Iterables.concat( + ImmutableList.of(updateMediaItemWithLocalConfiguration(existingItem)), + updateMediaItemsWithLocalConfiguration(mediaItems))) + .inOrder(); + assertThat(player.index).isEqualTo(1); } @Test @@ -556,22 +706,16 @@ public class MediaSessionCallbackTest { assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); } - private static class MockFillInLocalConfigurationCallback - implements MediaSession.MediaItemFiller { + private static MediaItem updateMediaItemWithLocalConfiguration(MediaItem mediaItem) { + return mediaItem.buildUpon().setUri(METADATA_MEDIA_URI).build(); + } - public final List mediaItemsFromParam = new ArrayList<>(); - public CountDownLatch latch; - - public MockFillInLocalConfigurationCallback(int latchCount) { - this.latch = new CountDownLatch(latchCount); - } - - @Override - public MediaItem fillInLocalConfiguration( - MediaSession session, ControllerInfo controllerInfo, MediaItem mediaItem) { - mediaItemsFromParam.add(mediaItem); - latch.countDown(); - return mediaItem; + private static List updateMediaItemsWithLocalConfiguration( + List mediaItems) { + ImmutableList.Builder listBuilder = ImmutableList.builder(); + for (int i = 0; i < mediaItems.size(); i++) { + listBuilder.add(updateMediaItemWithLocalConfiguration(mediaItems.get(i))); } + return listBuilder.build(); } } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionPlayerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionPlayerTest.java index 537026101c..1d5e92344f 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionPlayerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionPlayerTest.java @@ -31,6 +31,8 @@ import androidx.media3.test.session.common.TestUtils; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import java.util.List; import org.junit.After; import org.junit.Before; @@ -76,6 +78,14 @@ public class MediaSessionPlayerTest { } return MediaSession.ConnectionResult.reject(); } + + @Override + public ListenableFuture> onAddMediaItems( + MediaSession mediaSession, + MediaSession.ControllerInfo controller, + List mediaItems) { + return Futures.immediateFuture(mediaItems); + } }) .build(); @@ -197,7 +207,7 @@ public class MediaSessionPlayerTest { controller.setMediaItem(item); - player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); assertThat(player.mediaItems).containsExactly(item); assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.resetPosition).isEqualTo(resetPosition); @@ -213,7 +223,7 @@ public class MediaSessionPlayerTest { controller.setMediaItem(item); - player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); assertThat(player.mediaItems).containsExactly(item); assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.resetPosition).isEqualTo(resetPosition); @@ -229,7 +239,7 @@ public class MediaSessionPlayerTest { controller.setMediaItem(item); - player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEM, TIMEOUT_MS); + player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS); assertThat(player.mediaItems).containsExactly(item); assertThat(player.startPositionMs).isEqualTo(startPositionMs); assertThat(player.resetPosition).isEqualTo(resetPosition); @@ -317,7 +327,7 @@ public class MediaSessionPlayerTest { controller.addMediaItem(mediaItem); - player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM, TIMEOUT_MS); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS, TIMEOUT_MS); assertThat(player.mediaItems).hasSize(6); } @@ -328,7 +338,7 @@ public class MediaSessionPlayerTest { controller.addMediaItem(index, mediaItem); - player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM_WITH_INDEX, TIMEOUT_MS); + player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, TIMEOUT_MS); assertThat(player.index).isEqualTo(index); assertThat(player.mediaItems).hasSize(6); }