Commit 86cc9168 by tonihei Committed by Rohit Singh

Allow duplicated MediaItems in a legacy session

MediaItems are not meant to be unique in a playlist. If a legacy
session publishes multiple items that get converted to equal MediaItems,
the current code fails because we look up queue ids in a Map (that
doesn't allow duplicate entries).

Fix this by storing a simple list of items with additional data.

Issue: androidx/media#290
PiperOrigin-RevId: 521993802
(cherry picked from commit 219967c5)
parent 10e2dfd2
...@@ -1795,7 +1795,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1795,7 +1795,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
currentTimeline = currentTimeline =
isQueueChanged isQueueChanged
? QueueTimeline.create(newLegacyPlayerInfo.queue) ? QueueTimeline.create(newLegacyPlayerInfo.queue)
: new QueueTimeline((QueueTimeline) oldControllerInfo.playerInfo.timeline); : ((QueueTimeline) oldControllerInfo.playerInfo.timeline).copy();
boolean isMetadataCompatChanged = boolean isMetadataCompatChanged =
oldLegacyPlayerInfo.mediaMetadataCompat != newLegacyPlayerInfo.mediaMetadataCompat oldLegacyPlayerInfo.mediaMetadataCompat != newLegacyPlayerInfo.mediaMetadataCompat
...@@ -1987,8 +1987,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1987,8 +1987,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Integer mediaItemTransitionReason; Integer mediaItemTransitionReason;
boolean isOldTimelineEmpty = oldControllerInfo.playerInfo.timeline.isEmpty(); boolean isOldTimelineEmpty = oldControllerInfo.playerInfo.timeline.isEmpty();
boolean isNewTimelineEmpty = newControllerInfo.playerInfo.timeline.isEmpty(); boolean isNewTimelineEmpty = newControllerInfo.playerInfo.timeline.isEmpty();
int newCurrentMediaItemIndex =
newControllerInfo.playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex;
if (isOldTimelineEmpty && isNewTimelineEmpty) { if (isOldTimelineEmpty && isNewTimelineEmpty) {
// Still empty Timelines. // Still empty Timelines.
discontinuityReason = null; discontinuityReason = null;
...@@ -2000,13 +1998,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -2000,13 +1998,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} else { } else {
MediaItem oldCurrentMediaItem = MediaItem oldCurrentMediaItem =
checkStateNotNull(oldControllerInfo.playerInfo.getCurrentMediaItem()); checkStateNotNull(oldControllerInfo.playerInfo.getCurrentMediaItem());
int oldCurrentMediaItemIndexInNewTimeline = boolean oldCurrentMediaItemExistsInNewTimeline =
((QueueTimeline) newControllerInfo.playerInfo.timeline).indexOf(oldCurrentMediaItem); ((QueueTimeline) newControllerInfo.playerInfo.timeline).contains(oldCurrentMediaItem);
if (oldCurrentMediaItemIndexInNewTimeline == C.INDEX_UNSET) { if (!oldCurrentMediaItemExistsInNewTimeline) {
// Old current item is removed. // Old current item is removed.
discontinuityReason = Player.DISCONTINUITY_REASON_REMOVE; discontinuityReason = Player.DISCONTINUITY_REASON_REMOVE;
mediaItemTransitionReason = Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED; mediaItemTransitionReason = Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
} else if (oldCurrentMediaItemIndexInNewTimeline == newCurrentMediaItemIndex) { } else if (oldCurrentMediaItem.equals(newControllerInfo.playerInfo.getCurrentMediaItem())) {
// Current item is the same. // Current item is the same.
long oldCurrentPosition = long oldCurrentPosition =
MediaUtils.convertToCurrentPositionMs( MediaUtils.convertToCurrentPositionMs(
......
...@@ -78,6 +78,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; ...@@ -78,6 +78,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.truth.os.BundleSubject; import androidx.test.ext.truth.os.BundleSubject;
import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Range; import com.google.common.collect.Range;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
...@@ -415,6 +416,41 @@ public class MediaControllerWithMediaSessionCompatTest { ...@@ -415,6 +416,41 @@ public class MediaControllerWithMediaSessionCompatTest {
} }
@Test @Test
public void setQueue_withDuplicatedMediaItems_updatesAndNotifiesTimeline() throws Exception {
MediaController controller = controllerTestRule.createController(session.getSessionToken());
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Timeline> timelineFromParamRef = new AtomicReference<>();
AtomicReference<Timeline> timelineFromGetterRef = new AtomicReference<>();
AtomicInteger reasonRef = new AtomicInteger();
Player.Listener listener =
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
timelineFromParamRef.set(timeline);
timelineFromGetterRef.set(controller.getCurrentTimeline());
reasonRef.set(reason);
latch.countDown();
}
};
threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(/* size= */ 2);
Timeline testTimeline =
MediaTestUtils.createTimeline(
ImmutableList.copyOf(Iterables.concat(mediaItems, mediaItems)));
List<QueueItem> testQueue =
MediaTestUtils.convertToQueueItemsWithoutBitmap(
MediaUtils.convertToMediaItemList(testTimeline));
session.setQueue(testQueue);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
MediaTestUtils.assertMediaIdEquals(testTimeline, timelineFromParamRef.get());
MediaTestUtils.assertMediaIdEquals(testTimeline, timelineFromGetterRef.get());
assertThat(reasonRef.get()).isEqualTo(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
}
@Test
public void setQueue_withDescription_notifiesTimelineWithMetadata() throws Exception { public void setQueue_withDescription_notifiesTimelineWithMetadata() throws Exception {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Timeline> timelineRef = new AtomicReference<>(); AtomicReference<Timeline> timelineRef = new AtomicReference<>();
......
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