diff --git a/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt index 5c4e939c..d595a8a3 100644 --- a/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt +++ b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt @@ -19,6 +19,7 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.MediaSource +import androidx.media3.exoplayer.source.ShuffleOrder.DefaultShuffleOrder import androidx.media3.session.* import androidx.media3.session.MediaSession.ControllerInfo import com.cappielloantonio.tempo.R @@ -39,6 +40,7 @@ import com.google.common.util.concurrent.ListenableFuture @UnstableApi class MediaService : MediaLibraryService() { + private val TAG = "MediaService" private val librarySessionCallback = CustomMediaLibrarySessionCallback() private lateinit var player: ExoPlayer @@ -86,7 +88,7 @@ class MediaService : MediaLibraryService() { } fun updateMediaItems() { - Log.d("MediaService", "update items"); + Log.d(TAG, "update items"); val n = player.mediaItemCount val k = player.currentMediaItemIndex val current = player.currentPosition @@ -349,14 +351,30 @@ class MediaService : MediaLibraryService() { } override fun onTracksChanged(tracks: Tracks) { + Log.d(TAG, "onTracksChanged " + player.currentMediaItemIndex); ReplayGainUtil.setReplayGain(player, tracks) val currentMediaItem = player.currentMediaItem - if (currentMediaItem != null && currentMediaItem.mediaMetadata.extras != null) { - MediaManager.scrobble(currentMediaItem, false) + if (currentMediaItem != null) { + 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) - MediaManager.continuousPlay(player.currentMediaItem) + // https://stackoverflow.com/questions/56937283/exoplayer-shuffle-doesnt-reproduce-all-the-songs + 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) { diff --git a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java index f7cd8a38..62c6bccc 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java +++ b/app/src/main/java/com/cappielloantonio/tempo/service/MediaManager.java @@ -36,10 +36,12 @@ import com.google.common.util.concurrent.MoreExecutors; import java.lang.ref.WeakReference; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; public class MediaManager { private static final String TAG = "MediaManager"; private static WeakReference attachedBrowserRef = new WeakReference<>(null); + public static AtomicBoolean justStarted = new AtomicBoolean(false); public static void registerPlaybackObserver( ListenableFuture browserFuture, @@ -179,8 +181,8 @@ public class MediaManager { try { if (mediaBrowserListenableFuture.isDone()) { MediaBrowser browser = mediaBrowserListenableFuture.get(); - browser.clearMediaItems(); - browser.setMediaItems(MappingUtil.mapMediaItems(media)); + justStarted.set(true); + browser.setMediaItems(MappingUtil.mapMediaItems(media), startIndex, 0); browser.prepare(); Player.Listener timelineListener = new Player.Listener() { @@ -210,10 +212,11 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - mediaBrowserListenableFuture.get().clearMediaItems(); - mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(media)); - mediaBrowserListenableFuture.get().prepare(); - mediaBrowserListenableFuture.get().play(); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + justStarted.set(true); + browser.setMediaItem(MappingUtil.mapMediaItem(media)); + browser.prepare(); + browser.play(); enqueueDatabase(media, true, 0); } } catch (ExecutionException | InterruptedException e) { @@ -229,7 +232,7 @@ public class MediaManager { try { if (mediaBrowserListenableFuture.isDone()) { MediaBrowser mediaBrowser = mediaBrowserListenableFuture.get(); - mediaBrowser.clearMediaItems(); + justStarted.set(true); mediaBrowser.setMediaItem(mediaItem); mediaBrowser.prepare(); mediaBrowser.play(); @@ -247,10 +250,11 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - mediaBrowserListenableFuture.get().clearMediaItems(); - mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation)); - mediaBrowserListenableFuture.get().prepare(); - mediaBrowserListenableFuture.get().play(); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + justStarted.set(true); + browser.setMediaItem(MappingUtil.mapInternetRadioStation(internetRadioStation)); + browser.prepare(); + browser.play(); } } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); @@ -264,10 +268,11 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - mediaBrowserListenableFuture.get().clearMediaItems(); - mediaBrowserListenableFuture.get().setMediaItem(MappingUtil.mapMediaItem(podcastEpisode)); - mediaBrowserListenableFuture.get().prepare(); - mediaBrowserListenableFuture.get().play(); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + justStarted.set(true); + browser.setMediaItem(MappingUtil.mapMediaItem(podcastEpisode)); + browser.prepare(); + browser.play(); } } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); @@ -281,9 +286,11 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) { - enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex()); - mediaBrowserListenableFuture.get().addMediaItems(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItems(media)); + Log.e(TAG, "enqueue"); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) { + enqueueDatabase(media, false, browser.getNextMediaItemIndex()); + browser.addMediaItems(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItems(media)); } else { enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount()); mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media)); @@ -301,9 +308,11 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - if (playImmediatelyAfter && mediaBrowserListenableFuture.get().getNextMediaItemIndex() != -1) { - enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getNextMediaItemIndex()); - mediaBrowserListenableFuture.get().addMediaItem(mediaBrowserListenableFuture.get().getNextMediaItemIndex(), MappingUtil.mapMediaItem(media)); + Log.e(TAG, "enqueue"); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + if (playImmediatelyAfter && browser.getNextMediaItemIndex() != -1) { + enqueueDatabase(media, false, browser.getNextMediaItemIndex()); + browser.addMediaItem(browser.getNextMediaItemIndex(), MappingUtil.mapMediaItem(media)); } else { enqueueDatabase(media, false, mediaBrowserListenableFuture.get().getMediaItemCount()); mediaBrowserListenableFuture.get().addMediaItem(MappingUtil.mapMediaItem(media)); @@ -321,8 +330,10 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { - mediaBrowserListenableFuture.get().removeMediaItems(startIndex, endIndex + 1); - mediaBrowserListenableFuture.get().addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1)); + Log.e(TAG, "shuffle"); + MediaBrowser browser = mediaBrowserListenableFuture.get(); + browser.removeMediaItems(startIndex, endIndex + 1); + browser.addMediaItems(MappingUtil.mapMediaItems(media).subList(startIndex, endIndex + 1)); swapDatabase(media); } } catch (ExecutionException | InterruptedException e) { @@ -337,6 +348,7 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { + Log.e(TAG, "swap"); mediaBrowserListenableFuture.get().moveMediaItem(from, to); swapDatabase(media); } @@ -352,6 +364,7 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { + Log.e(TAG, "remove"); if (mediaBrowserListenableFuture.get().getMediaItemCount() > 1 && mediaBrowserListenableFuture.get().getCurrentMediaItemIndex() != toRemove) { mediaBrowserListenableFuture.get().removeMediaItem(toRemove); removeDatabase(media, toRemove); @@ -371,6 +384,7 @@ public class MediaManager { mediaBrowserListenableFuture.addListener(() -> { try { if (mediaBrowserListenableFuture.isDone()) { + Log.e(TAG, "remove range"); mediaBrowserListenableFuture.get().removeMediaItems(fromItem, toItem); removeRangeDatabase(media, fromItem, toItem); } @@ -420,6 +434,7 @@ public class MediaManager { @Override public void onChanged(List media) { if (media != null) { + Log.e(TAG, "continuous play"); ListenableFuture mediaBrowserListenableFuture = new MediaBrowser.Builder( App.getContext(), new SessionToken(App.getContext(), new ComponentName(App.getContext(), MediaService.class))