From e8077fb3f4a8f970e8d1314975b42ee8f746804f Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 22 Feb 2019 16:29:36 +0000 Subject: [PATCH] honour shuffle order when publishing queue to media session Issue #5360 PiperOrigin-RevId: 235196177 --- RELEASENOTES.md | 2 + .../mediasession/MediaSessionConnector.java | 1 + .../mediasession/TimelineQueueNavigator.java | 53 ++++++++++++++----- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 43a4f7d1c4..a73b56a7fe 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -60,6 +60,8 @@ `TrackSelectionDialogBuilder` and add option to select multiple overrides. * MediaSessionConnector: Let apps intercept media button events ([#5179](https://github.com/google/ExoPlayer/issues/5179)). +* Fix issue with `TimelineQueueNavigator` not publishing the queue in shuffled + order when in shuffle mode. ### 2.9.6 ### diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java index 6d80c1001f..617423a399 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java @@ -971,6 +971,7 @@ public final class MediaSessionConnector { ? PlaybackStateCompat.SHUFFLE_MODE_ALL : PlaybackStateCompat.SHUFFLE_MODE_NONE); invalidateMediaSessionPlaybackState(); + invalidateMediaSessionQueue(); } @Override diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java index 8575a74c70..6e61ad2fe2 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java @@ -25,10 +25,10 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ControlDispatcher; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.util.Assertions; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; -import java.util.List; /** * An abstract implementation of the {@link MediaSessionConnector.QueueNavigator} that maps the @@ -67,6 +67,7 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu * @param maxQueueSize The maximum queue size. */ public TimelineQueueNavigator(MediaSessionCompat mediaSession, int maxQueueSize) { + Assertions.checkState(maxQueueSize > 0); this.mediaSession = mediaSession; this.maxQueueSize = maxQueueSize; activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; @@ -193,22 +194,50 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu // Helper methods. private void publishFloatingQueueWindow(Player player) { - if (player.getCurrentTimeline().isEmpty()) { + Timeline timeline = player.getCurrentTimeline(); + if (timeline.isEmpty()) { mediaSession.setQueue(Collections.emptyList()); activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; return; } - int windowCount = player.getCurrentTimeline().getWindowCount(); + ArrayDeque queue = new ArrayDeque<>(); + int queueSize = Math.min(maxQueueSize, timeline.getWindowCount()); + + // Add the active queue item. int currentWindowIndex = player.getCurrentWindowIndex(); - int queueSize = Math.min(maxQueueSize, windowCount); - int startIndex = Util.constrainValue(currentWindowIndex - ((queueSize - 1) / 2), 0, - windowCount - queueSize); - List queue = new ArrayList<>(); - for (int i = startIndex; i < startIndex + queueSize; i++) { - queue.add(new MediaSessionCompat.QueueItem(getMediaDescription(player, i), i)); + queue.add( + new MediaSessionCompat.QueueItem( + getMediaDescription(player, currentWindowIndex), currentWindowIndex)); + + // Fill queue alternating with next and/or previous queue items. + int firstWindowIndex = currentWindowIndex; + int lastWindowIndex = currentWindowIndex; + boolean shuffleModeEnabled = player.getShuffleModeEnabled(); + while ((firstWindowIndex != C.INDEX_UNSET || lastWindowIndex != C.INDEX_UNSET) + && queue.size() < queueSize) { + // Begin with next to have a longer tail than head if an even sized queue needs to be trimmed. + if (lastWindowIndex != C.INDEX_UNSET) { + lastWindowIndex = + timeline.getNextWindowIndex( + lastWindowIndex, Player.REPEAT_MODE_OFF, shuffleModeEnabled); + if (lastWindowIndex != C.INDEX_UNSET) { + queue.add( + new MediaSessionCompat.QueueItem( + getMediaDescription(player, lastWindowIndex), lastWindowIndex)); + } + } + if (firstWindowIndex != C.INDEX_UNSET && queue.size() < queueSize) { + firstWindowIndex = + timeline.getPreviousWindowIndex( + firstWindowIndex, Player.REPEAT_MODE_OFF, shuffleModeEnabled); + if (firstWindowIndex != C.INDEX_UNSET) { + queue.addFirst( + new MediaSessionCompat.QueueItem( + getMediaDescription(player, firstWindowIndex), firstWindowIndex)); + } + } } - mediaSession.setQueue(queue); + mediaSession.setQueue(new ArrayList<>(queue)); activeQueueItemId = currentWindowIndex; } - }