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. #minor-release Issue: androidx/media#290 PiperOrigin-RevId: 521993802
This commit is contained in:
parent
e4cb583af2
commit
219967c5a3
@ -28,6 +28,10 @@
|
|||||||
instead. Note that even for the deprecated variants, the offset is not
|
instead. Note that even for the deprecated variants, the offset is not
|
||||||
anymore added to `startTimeUs` and `endTimeUs` of the `MediaLoadData`
|
anymore added to `startTimeUs` and `endTimeUs` of the `MediaLoadData`
|
||||||
objects that are dispatched by the dispatcher.
|
objects that are dispatched by the dispatcher.
|
||||||
|
* Session:
|
||||||
|
* Fix bug where multiple identical queue items published by a legacy
|
||||||
|
`MediaSessionCompat` result in an exception in `MediaController`
|
||||||
|
([#290](https://github.com/androidx/media/issues/290)).
|
||||||
* Audio:
|
* Audio:
|
||||||
* Fix bug where some playbacks fail when tunneling is enabled and
|
* Fix bug where some playbacks fail when tunneling is enabled and
|
||||||
`AudioProcessors` are active, e.g. for gapless trimming
|
`AudioProcessors` are active, e.g. for gapless trimming
|
||||||
|
@ -1796,7 +1796,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
|
||||||
@ -1988,8 +1988,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;
|
||||||
@ -2001,13 +1999,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(
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package androidx.media3.session;
|
package androidx.media3.session;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
|
|
||||||
import android.support.v4.media.MediaMetadataCompat;
|
import android.support.v4.media.MediaMetadataCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
|
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
|
||||||
@ -27,11 +26,8 @@ import androidx.media3.common.Timeline;
|
|||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An immutable class to represent the current {@link Timeline} backed by {@linkplain QueueItem
|
* An immutable class to represent the current {@link Timeline} backed by {@linkplain QueueItem
|
||||||
@ -45,42 +41,33 @@ import java.util.Map;
|
|||||||
/* package */ final class QueueTimeline extends Timeline {
|
/* package */ final class QueueTimeline extends Timeline {
|
||||||
|
|
||||||
public static final QueueTimeline DEFAULT =
|
public static final QueueTimeline DEFAULT =
|
||||||
new QueueTimeline(ImmutableList.of(), ImmutableMap.of(), /* fakeMediaItem= */ null);
|
new QueueTimeline(ImmutableList.of(), /* fakeMediaItem= */ null);
|
||||||
|
|
||||||
private static final Object FAKE_WINDOW_UID = new Object();
|
private static final Object FAKE_WINDOW_UID = new Object();
|
||||||
|
|
||||||
private final ImmutableList<MediaItem> mediaItems;
|
private final ImmutableList<QueuedMediaItem> queuedMediaItems;
|
||||||
private final ImmutableMap<MediaItem, Long> mediaItemToQueueIdMap;
|
|
||||||
@Nullable private final MediaItem fakeMediaItem;
|
@Nullable private final MediaItem fakeMediaItem;
|
||||||
|
|
||||||
/** Creates a new instance. */
|
|
||||||
public QueueTimeline(QueueTimeline queueTimeline) {
|
|
||||||
this.mediaItems = queueTimeline.mediaItems;
|
|
||||||
this.mediaItemToQueueIdMap = queueTimeline.mediaItemToQueueIdMap;
|
|
||||||
this.fakeMediaItem = queueTimeline.fakeMediaItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private QueueTimeline(
|
private QueueTimeline(
|
||||||
ImmutableList<MediaItem> mediaItems,
|
ImmutableList<QueuedMediaItem> queuedMediaItems, @Nullable MediaItem fakeMediaItem) {
|
||||||
ImmutableMap<MediaItem, Long> mediaItemToQueueIdMap,
|
this.queuedMediaItems = queuedMediaItems;
|
||||||
@Nullable MediaItem fakeMediaItem) {
|
|
||||||
this.mediaItems = mediaItems;
|
|
||||||
this.mediaItemToQueueIdMap = mediaItemToQueueIdMap;
|
|
||||||
this.fakeMediaItem = fakeMediaItem;
|
this.fakeMediaItem = fakeMediaItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link QueueTimeline} from a list of {@linkplain QueueItem queue items}. */
|
/** Creates a {@link QueueTimeline} from a list of {@linkplain QueueItem queue items}. */
|
||||||
public static QueueTimeline create(List<QueueItem> queue) {
|
public static QueueTimeline create(List<QueueItem> queue) {
|
||||||
ImmutableList.Builder<MediaItem> mediaItemsBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<QueuedMediaItem> queuedMediaItemsBuilder = new ImmutableList.Builder<>();
|
||||||
ImmutableMap.Builder<MediaItem, Long> mediaItemToQueueIdMap = new ImmutableMap.Builder<>();
|
|
||||||
for (int i = 0; i < queue.size(); i++) {
|
for (int i = 0; i < queue.size(); i++) {
|
||||||
QueueItem queueItem = queue.get(i);
|
QueueItem queueItem = queue.get(i);
|
||||||
MediaItem mediaItem = MediaUtils.convertToMediaItem(queueItem);
|
MediaItem mediaItem = MediaUtils.convertToMediaItem(queueItem);
|
||||||
mediaItemsBuilder.add(mediaItem);
|
queuedMediaItemsBuilder.add(new QueuedMediaItem(mediaItem, queueItem.getQueueId()));
|
||||||
mediaItemToQueueIdMap.put(mediaItem, queueItem.getQueueId());
|
|
||||||
}
|
}
|
||||||
return new QueueTimeline(
|
return new QueueTimeline(queuedMediaItemsBuilder.build(), /* fakeMediaItem= */ null);
|
||||||
mediaItemsBuilder.build(), mediaItemToQueueIdMap.buildOrThrow(), /* fakeMediaItem= */ null);
|
}
|
||||||
|
|
||||||
|
/** Returns a copy of the current queue timeline. */
|
||||||
|
public QueueTimeline copy() {
|
||||||
|
return new QueueTimeline(queuedMediaItems, fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,9 +78,9 @@ import java.util.Map;
|
|||||||
* @return The corresponding queue ID or {@link QueueItem#UNKNOWN_ID} if not known.
|
* @return The corresponding queue ID or {@link QueueItem#UNKNOWN_ID} if not known.
|
||||||
*/
|
*/
|
||||||
public long getQueueId(int mediaItemIndex) {
|
public long getQueueId(int mediaItemIndex) {
|
||||||
MediaItem mediaItem = getMediaItemAt(mediaItemIndex);
|
return mediaItemIndex >= 0 && mediaItemIndex < queuedMediaItems.size()
|
||||||
@Nullable Long queueId = mediaItemToQueueIdMap.get(mediaItem);
|
? queuedMediaItems.get(mediaItemIndex).queueId
|
||||||
return queueId == null ? QueueItem.UNKNOWN_ID : queueId;
|
: QueueItem.UNKNOWN_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +90,7 @@ import java.util.Map;
|
|||||||
* @return A new {@link QueueTimeline} reflecting the update.
|
* @return A new {@link QueueTimeline} reflecting the update.
|
||||||
*/
|
*/
|
||||||
public QueueTimeline copyWithFakeMediaItem(@Nullable MediaItem fakeMediaItem) {
|
public QueueTimeline copyWithFakeMediaItem(@Nullable MediaItem fakeMediaItem) {
|
||||||
return new QueueTimeline(mediaItems, mediaItemToQueueIdMap, fakeMediaItem);
|
return new QueueTimeline(queuedMediaItems, fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,23 +102,17 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public QueueTimeline copyWithNewMediaItem(int replaceIndex, MediaItem newMediaItem) {
|
public QueueTimeline copyWithNewMediaItem(int replaceIndex, MediaItem newMediaItem) {
|
||||||
checkArgument(
|
checkArgument(
|
||||||
replaceIndex < mediaItems.size()
|
replaceIndex < queuedMediaItems.size()
|
||||||
|| (replaceIndex == mediaItems.size() && fakeMediaItem != null));
|
|| (replaceIndex == queuedMediaItems.size() && fakeMediaItem != null));
|
||||||
if (replaceIndex == mediaItems.size()) {
|
if (replaceIndex == queuedMediaItems.size()) {
|
||||||
return new QueueTimeline(mediaItems, mediaItemToQueueIdMap, newMediaItem);
|
return new QueueTimeline(queuedMediaItems, newMediaItem);
|
||||||
}
|
}
|
||||||
MediaItem oldMediaItem = mediaItems.get(replaceIndex);
|
long queueId = queuedMediaItems.get(replaceIndex).queueId;
|
||||||
// Create the new play list.
|
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
|
||||||
ImmutableList.Builder<MediaItem> newMediaItemsBuilder = new ImmutableList.Builder<>();
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, replaceIndex));
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(0, replaceIndex));
|
queuedItemsBuilder.add(new QueuedMediaItem(newMediaItem, queueId));
|
||||||
newMediaItemsBuilder.add(newMediaItem);
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(replaceIndex + 1, queuedMediaItems.size()));
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(replaceIndex + 1, mediaItems.size()));
|
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
|
||||||
// Update the map of items to queue IDs accordingly.
|
|
||||||
Map<MediaItem, Long> newMediaItemToQueueIdMap = new HashMap<>(mediaItemToQueueIdMap);
|
|
||||||
Long queueId = checkNotNull(newMediaItemToQueueIdMap.remove(oldMediaItem));
|
|
||||||
newMediaItemToQueueIdMap.put(newMediaItem, queueId);
|
|
||||||
return new QueueTimeline(
|
|
||||||
newMediaItemsBuilder.build(), ImmutableMap.copyOf(newMediaItemToQueueIdMap), fakeMediaItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,11 +124,13 @@ import java.util.Map;
|
|||||||
* @return A new {@link QueueTimeline} reflecting the update.
|
* @return A new {@link QueueTimeline} reflecting the update.
|
||||||
*/
|
*/
|
||||||
public QueueTimeline copyWithNewMediaItems(int index, List<MediaItem> newMediaItems) {
|
public QueueTimeline copyWithNewMediaItems(int index, List<MediaItem> newMediaItems) {
|
||||||
ImmutableList.Builder<MediaItem> newMediaItemsBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(0, index));
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, index));
|
||||||
newMediaItemsBuilder.addAll(newMediaItems);
|
for (int i = 0; i < newMediaItems.size(); i++) {
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(index, mediaItems.size()));
|
queuedItemsBuilder.add(new QueuedMediaItem(newMediaItems.get(i), QueueItem.UNKNOWN_ID));
|
||||||
return new QueueTimeline(newMediaItemsBuilder.build(), mediaItemToQueueIdMap, fakeMediaItem);
|
}
|
||||||
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(index, queuedMediaItems.size()));
|
||||||
|
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,10 +141,10 @@ import java.util.Map;
|
|||||||
* @return A new {@link QueueTimeline} reflecting the update.
|
* @return A new {@link QueueTimeline} reflecting the update.
|
||||||
*/
|
*/
|
||||||
public QueueTimeline copyWithRemovedMediaItems(int fromIndex, int toIndex) {
|
public QueueTimeline copyWithRemovedMediaItems(int fromIndex, int toIndex) {
|
||||||
ImmutableList.Builder<MediaItem> newMediaItemsBuilder = new ImmutableList.Builder<>();
|
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(0, fromIndex));
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, fromIndex));
|
||||||
newMediaItemsBuilder.addAll(mediaItems.subList(toIndex, mediaItems.size()));
|
queuedItemsBuilder.addAll(queuedMediaItems.subList(toIndex, queuedMediaItems.size()));
|
||||||
return new QueueTimeline(newMediaItemsBuilder.build(), mediaItemToQueueIdMap, fakeMediaItem);
|
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,50 +156,45 @@ import java.util.Map;
|
|||||||
* @return A new {@link QueueTimeline} reflecting the update.
|
* @return A new {@link QueueTimeline} reflecting the update.
|
||||||
*/
|
*/
|
||||||
public QueueTimeline copyWithMovedMediaItems(int fromIndex, int toIndex, int newIndex) {
|
public QueueTimeline copyWithMovedMediaItems(int fromIndex, int toIndex, int newIndex) {
|
||||||
List<MediaItem> list = new ArrayList<>(mediaItems);
|
List<QueuedMediaItem> list = new ArrayList<>(queuedMediaItems);
|
||||||
Util.moveItems(list, fromIndex, toIndex, newIndex);
|
Util.moveItems(list, fromIndex, toIndex, newIndex);
|
||||||
return new QueueTimeline(
|
return new QueueTimeline(ImmutableList.copyOf(list), fakeMediaItem);
|
||||||
new ImmutableList.Builder<MediaItem>().addAll(list).build(),
|
|
||||||
mediaItemToQueueIdMap,
|
|
||||||
fakeMediaItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns whether the timeline contains the given {@link MediaItem}. */
|
||||||
* Returns the media item index of the given media item in the timeline, or {@link C#INDEX_UNSET}
|
public boolean contains(MediaItem mediaItem) {
|
||||||
* if the item is not part of this timeline.
|
|
||||||
*
|
|
||||||
* @param mediaItem The media item of interest.
|
|
||||||
* @return The index of the item or {@link C#INDEX_UNSET} if the item is not part of the timeline.
|
|
||||||
*/
|
|
||||||
public int indexOf(MediaItem mediaItem) {
|
|
||||||
if (mediaItem.equals(fakeMediaItem)) {
|
if (mediaItem.equals(fakeMediaItem)) {
|
||||||
return mediaItems.size();
|
return true;
|
||||||
}
|
}
|
||||||
int mediaItemIndex = mediaItems.indexOf(mediaItem);
|
for (int i = 0; i < queuedMediaItems.size(); i++) {
|
||||||
return mediaItemIndex == -1 ? C.INDEX_UNSET : mediaItemIndex;
|
if (mediaItem.equals(queuedMediaItems.get(i).mediaItem)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public MediaItem getMediaItemAt(int mediaItemIndex) {
|
public MediaItem getMediaItemAt(int mediaItemIndex) {
|
||||||
if (mediaItemIndex >= 0 && mediaItemIndex < mediaItems.size()) {
|
if (mediaItemIndex >= 0 && mediaItemIndex < queuedMediaItems.size()) {
|
||||||
return mediaItems.get(mediaItemIndex);
|
return queuedMediaItems.get(mediaItemIndex).mediaItem;
|
||||||
}
|
}
|
||||||
return (mediaItemIndex == mediaItems.size()) ? fakeMediaItem : null;
|
return (mediaItemIndex == queuedMediaItems.size()) ? fakeMediaItem : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWindowCount() {
|
public int getWindowCount() {
|
||||||
return mediaItems.size() + ((fakeMediaItem == null) ? 0 : 1);
|
return queuedMediaItems.size() + ((fakeMediaItem == null) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||||
// TODO(b/149713425): Set duration if it's available from MediaMetadataCompat.
|
// TODO(b/149713425): Set duration if it's available from MediaMetadataCompat.
|
||||||
MediaItem mediaItem;
|
MediaItem mediaItem;
|
||||||
if (windowIndex == mediaItems.size() && fakeMediaItem != null) {
|
if (windowIndex == queuedMediaItems.size() && fakeMediaItem != null) {
|
||||||
mediaItem = fakeMediaItem;
|
mediaItem = fakeMediaItem;
|
||||||
} else {
|
} else {
|
||||||
mediaItem = mediaItems.get(windowIndex);
|
mediaItem = queuedMediaItems.get(windowIndex).mediaItem;
|
||||||
}
|
}
|
||||||
return getWindow(window, mediaItem, windowIndex);
|
return getWindow(window, mediaItem, windowIndex);
|
||||||
}
|
}
|
||||||
@ -257,14 +235,13 @@ import java.util.Map;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QueueTimeline other = (QueueTimeline) obj;
|
QueueTimeline other = (QueueTimeline) obj;
|
||||||
return Objects.equal(mediaItems, other.mediaItems)
|
return Objects.equal(queuedMediaItems, other.queuedMediaItems)
|
||||||
&& Objects.equal(mediaItemToQueueIdMap, other.mediaItemToQueueIdMap)
|
|
||||||
&& Objects.equal(fakeMediaItem, other.fakeMediaItem);
|
&& Objects.equal(fakeMediaItem, other.fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(mediaItems, mediaItemToQueueIdMap, fakeMediaItem);
|
return Objects.hashCode(queuedMediaItems, fakeMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Window getWindow(Window window, MediaItem mediaItem, int windowIndex) {
|
private static Window getWindow(Window window, MediaItem mediaItem, int windowIndex) {
|
||||||
@ -285,4 +262,35 @@ import java.util.Map;
|
|||||||
/* positionInFirstPeriodUs= */ 0);
|
/* positionInFirstPeriodUs= */ 0);
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class QueuedMediaItem {
|
||||||
|
|
||||||
|
public final MediaItem mediaItem;
|
||||||
|
public final long queueId;
|
||||||
|
|
||||||
|
public QueuedMediaItem(MediaItem mediaItem, long queueId) {
|
||||||
|
this.mediaItem = mediaItem;
|
||||||
|
this.queueId = queueId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof QueuedMediaItem)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QueuedMediaItem that = (QueuedMediaItem) o;
|
||||||
|
return queueId == that.queueId && mediaItem.equals(that.mediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 7;
|
||||||
|
result = 31 * result + (int) (queueId ^ (queueId >>> 32));
|
||||||
|
result = 31 * result + mediaItem.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,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 {
|
|||||||
assertThat(timelineRef.get().getPeriodCount()).isEqualTo(0);
|
assertThat(timelineRef.get().getPeriodCount()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user