fix shuffle

This commit is contained in:
pca006132 2025-11-16 14:19:58 +08:00
parent 6698052ba5
commit 5a8a631449
2 changed files with 61 additions and 28 deletions

View file

@ -19,6 +19,7 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.DefaultLoadControl
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.exoplayer.source.ShuffleOrder.DefaultShuffleOrder
import androidx.media3.session.* import androidx.media3.session.*
import androidx.media3.session.MediaSession.ControllerInfo import androidx.media3.session.MediaSession.ControllerInfo
import com.cappielloantonio.tempo.R import com.cappielloantonio.tempo.R
@ -39,6 +40,7 @@ import com.google.common.util.concurrent.ListenableFuture
@UnstableApi @UnstableApi
class MediaService : MediaLibraryService() { class MediaService : MediaLibraryService() {
private val TAG = "MediaService"
private val librarySessionCallback = CustomMediaLibrarySessionCallback() private val librarySessionCallback = CustomMediaLibrarySessionCallback()
private lateinit var player: ExoPlayer private lateinit var player: ExoPlayer
@ -86,7 +88,7 @@ class MediaService : MediaLibraryService() {
} }
fun updateMediaItems() { fun updateMediaItems() {
Log.d("MediaService", "update items"); Log.d(TAG, "update items");
val n = player.mediaItemCount val n = player.mediaItemCount
val k = player.currentMediaItemIndex val k = player.currentMediaItemIndex
val current = player.currentPosition val current = player.currentPosition
@ -349,14 +351,30 @@ class MediaService : MediaLibraryService() {
} }
override fun onTracksChanged(tracks: Tracks) { override fun onTracksChanged(tracks: Tracks) {
Log.d(TAG, "onTracksChanged " + player.currentMediaItemIndex);
ReplayGainUtil.setReplayGain(player, tracks) ReplayGainUtil.setReplayGain(player, tracks)
val currentMediaItem = player.currentMediaItem val currentMediaItem = player.currentMediaItem
if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) { if (currentMediaItem != null) {
MediaManager.scrobble(currentMediaItem, false) val item = MappingUtil.mapMediaItem(currentMediaItem)
if (item.mediaMetadata.extras != null)
MediaManager.scrobble(item, false)
if (player.nextMediaItemIndex == C.INDEX_UNSET)
MediaManager.continuousPlay(player.currentMediaItem)
} }
if (player.currentMediaItemIndex + 1 == player.mediaItemCount) // https://stackoverflow.com/questions/56937283/exoplayer-shuffle-doesnt-reproduce-all-the-songs
MediaManager.continuousPlay(player.currentMediaItem) if (MediaManager.justStarted.get()) {
Log.d(TAG, "update shuffle order")
MediaManager.justStarted.set(false)
val shuffledList = IntArray(player.mediaItemCount) { i -> i }
shuffledList.shuffle()
val index = shuffledList.indexOf(player.currentMediaItemIndex)
// swap current media index to the first index
if (index > -1 && shuffledList.isNotEmpty())
run { val tmp = shuffledList[0]; shuffledList[0] = shuffledList[index]; shuffledList[index] = tmp}
player.shuffleOrder = DefaultShuffleOrder(shuffledList, kotlin.random.Random.nextLong())
}
} }
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {

View file

@ -36,10 +36,12 @@ import com.google.common.util.concurrent.MoreExecutors;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
public class MediaManager { public class MediaManager {
private static final String TAG = "MediaManager"; private static final String TAG = "MediaManager";
private static WeakReference<MediaBrowser> attachedBrowserRef = new WeakReference<>(null); private static WeakReference<MediaBrowser> attachedBrowserRef = new WeakReference<>(null);
public static AtomicBoolean justStarted = new AtomicBoolean(false);
public static void registerPlaybackObserver( public static void registerPlaybackObserver(
ListenableFuture<MediaBrowser> browserFuture, ListenableFuture<MediaBrowser> browserFuture,
@ -179,8 +181,8 @@ public class MediaManager {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
MediaBrowser browser = mediaBrowserListenableFuture.get(); MediaBrowser browser = mediaBrowserListenableFuture.get();
browser.clearMediaItems(); justStarted.set(true);
browser.setMediaItems(MappingUtil.mapMediaItems(media)); browser.setMediaItems(MappingUtil.mapMediaItems(media), startIndex, 0);
browser.prepare(); browser.prepare();
Player.Listener timelineListener = new Player.Listener() { Player.Listener timelineListener = new Player.Listener() {
@ -210,10 +212,11 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems(); MediaBrowser browser = mediaBrowserListenableFuture.get();
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(media)); justStarted.set(true);
mediaBrowserListenableFuture.get().prepare(); browser.setMediaItem(MappingUtil.mapMediaItem(media));
mediaBrowserListenableFuture.get().play(); browser.prepare();
browser.play();
enqueueDatabase(media, true, 0); enqueueDatabase(media, true, 0);
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
@ -229,7 +232,7 @@ public class MediaManager {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get(); MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get();
mediaBrowser.clearMediaItems(); justStarted.set(true);
mediaBrowser.setMediaItem(mediaItem); mediaBrowser.setMediaItem(mediaItem);
mediaBrowser.prepare(); mediaBrowser.prepare();
mediaBrowser.play(); mediaBrowser.play();
@ -247,10 +250,11 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems(); MediaBrowser browser = mediaBrowserListenableFuture.get();
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation)); justStarted.set(true);
mediaBrowserListenableFuture.get().prepare(); browser.setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation));
mediaBrowserListenableFuture.get().play(); browser.prepare();
browser.play();
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
@ -264,10 +268,11 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().clearMediaItems(); MediaBrowser browser = mediaBrowserListenableFuture.get();
mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(podcastEpisode)); justStarted.set(true);
mediaBrowserListenableFuture.get().prepare(); browser.setMediaItem(MappingUtil.mapMediaItem(podcastEpisode));
mediaBrowserListenableFuture.get().play(); browser.prepare();
browser.play();
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
@ -281,9 +286,11 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) { Log.e(TAG, "enqueue");
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex()); MediaBrowser browser = mediaBrowserListenableFuture.get();
mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(media)); if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) {
enqueueDatabase(media, false, browser.getNextMediaItemIndex());
browser.addMediaItems(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItems(media));
} else { } else {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount()); enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media)); mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media));
@ -301,9 +308,11 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) { Log.e(TAG, "enqueue");
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex()); MediaBrowser browser = mediaBrowserListenableFuture.get();
mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(media)); if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) {
enqueueDatabase(media, false, browser.getNextMediaItemIndex());
browser.addMediaItem(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItem(media));
} else { } else {
enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount()); enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount());
mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media)); mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media));
@ -321,8 +330,10 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
mediaBrowserListenableFuture.get().removeMediaItems(startIndex, endIndex + 1); Log.e(TAG, "shuffle");
mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1)); MediaBrowser browser = mediaBrowserListenableFuture.get();
browser.removeMediaItems(startIndex, endIndex + 1);
browser.addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1));
swapDatabase(media); swapDatabase(media);
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
@ -337,6 +348,7 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
Log.e(TAG, "swap");
mediaBrowserListenableFuture.get().moveMediaItem(from, to); mediaBrowserListenableFuture.get().moveMediaItem(from, to);
swapDatabase(media); swapDatabase(media);
} }
@ -352,6 +364,7 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
Log.e(TAG, "remove");
if (mediaBrowserListenableFuture.get().getMediaItemCount() > 1 && mediaBrowserListenableFuture.get().getCurrentMediaItemIndex() != toRemove) { if (mediaBrowserListenableFuture.get().getMediaItemCount() > 1 && mediaBrowserListenableFuture.get().getCurrentMediaItemIndex() != toRemove) {
mediaBrowserListenableFuture.get().removeMediaItem(toRemove); mediaBrowserListenableFuture.get().removeMediaItem(toRemove);
removeDatabase(media, toRemove); removeDatabase(media, toRemove);
@ -371,6 +384,7 @@ public class MediaManager {
mediaBrowserListenableFuture.addListener(() -> { mediaBrowserListenableFuture.addListener(() -> {
try { try {
if (mediaBrowserListenableFuture.isDone()) { if (mediaBrowserListenableFuture.isDone()) {
Log.e(TAG, "remove range");
mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem); mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem);
removeRangeDatabase(media, fromItem, toItem); removeRangeDatabase(media, fromItem, toItem);
} }
@ -420,6 +434,7 @@ public class MediaManager {
@Override @Override
public void onChanged(List<Child> media) { public void onChanged(List<Child> media) {
if (media != null) { if (media != null) {
Log.e(TAG, "continuous play");
ListenableFuture<MediaBrowser> mediaBrowserListenableFuture = new MediaBrowser.Builder( ListenableFuture<MediaBrowser> mediaBrowserListenableFuture = new MediaBrowser.Builder(
App.getContext(), App.getContext(),
new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class)) new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class))