fix(audio-player): android shuffle wasnt working

This commit is contained in:
Kingkor Roy Tirtho 2026-10-16 14:36:27 +06:00
parent da394d40ea
commit c9d16a25c6

View File

@ -84,6 +84,8 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
private var disposed = false private var disposed = false
private val currentPlaylist = mutableListOf<MediaItem>() private val currentPlaylist = mutableListOf<MediaItem>()
private val originalPlaylist = mutableListOf<MediaItem>()
private var shuffleEnabled = false
private val urlIndexMap = mutableMapOf<String, Int>() private val urlIndexMap = mutableMapOf<String, Int>()
private val _playerState = MutableStateFlow(PlayerState.IDLE) private val _playerState = MutableStateFlow(PlayerState.IDLE)
@ -143,10 +145,6 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
) )
} }
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
_shuffleMode.tryEmit(shuffleModeEnabled)
}
override fun onVolumeChanged(volume: Float) { override fun onVolumeChanged(volume: Float) {
_volume.tryEmit(volume) _volume.tryEmit(volume)
} }
@ -260,8 +258,37 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
actual override suspend fun shuffle(enabled: Boolean) { actual override suspend fun shuffle(enabled: Boolean) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
exoPlayer.shuffleModeEnabled = enabled if (disposed || currentPlaylist.isEmpty() || shuffleEnabled == enabled) return@withContext
val currentIndex = exoPlayer.currentMediaItemIndex
val current = currentPlaylist.getOrNull(currentIndex) ?: return@withContext
val rest = if (enabled) {
originalPlaylist.filterNot { it.url == current.url }.shuffled()
} else {
originalPlaylist.filterNot { it.url == current.url }
}
// Disabling restores original order around the current item.
val before = if (enabled) emptyList() else originalPlaylist.takeWhile { it.url != current.url }
val after = if (enabled) rest else originalPlaylist.dropWhile { it.url != current.url }.drop(1)
// Reorder in place around the playing item so playback is not interrupted.
if (currentIndex < exoPlayer.mediaItemCount - 1) {
exoPlayer.removeMediaItems(currentIndex + 1, exoPlayer.mediaItemCount)
}
if (currentIndex > 0) exoPlayer.removeMediaItems(0, currentIndex)
exoPlayer.addMediaItems(0, before.map { it.toExoMediaItem() })
exoPlayer.addMediaItems(after.map { it.toExoMediaItem() })
currentPlaylist.clear()
currentPlaylist.addAll(before + current + after)
urlIndexMap.clear()
currentPlaylist.forEachIndexed { i, item -> urlIndexMap[item.url] = i }
shuffleEnabled = enabled
_shuffleMode.tryEmit(enabled) _shuffleMode.tryEmit(enabled)
_playlist.tryEmit(currentPlaylist.toList())
syncCurrentItem()
} }
} }
@ -273,6 +300,10 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
currentPlaylist.clear() currentPlaylist.clear()
currentPlaylist.addAll(playlist) currentPlaylist.addAll(playlist)
originalPlaylist.clear()
originalPlaylist.addAll(playlist)
shuffleEnabled = false
_shuffleMode.tryEmit(false)
urlIndexMap.clear() urlIndexMap.clear()
playlist.forEachIndexed { index, item -> playlist.forEachIndexed { index, item ->
urlIndexMap[item.url] = index urlIndexMap[item.url] = index
@ -305,6 +336,7 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
actual override suspend fun addMediaItem(mediaItem: MediaItem) { actual override suspend fun addMediaItem(mediaItem: MediaItem) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
currentPlaylist.add(mediaItem) currentPlaylist.add(mediaItem)
originalPlaylist.add(mediaItem)
urlIndexMap[mediaItem.url] = currentPlaylist.lastIndex urlIndexMap[mediaItem.url] = currentPlaylist.lastIndex
_playlist.tryEmit(currentPlaylist.toList()) _playlist.tryEmit(currentPlaylist.toList())
exoPlayer.addMediaItem(mediaItem.toExoMediaItem()) exoPlayer.addMediaItem(mediaItem.toExoMediaItem())
@ -317,6 +349,8 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
val insertIndex = (currentIndex + 1).coerceAtMost(currentPlaylist.size) val insertIndex = (currentIndex + 1).coerceAtMost(currentPlaylist.size)
currentPlaylist.add(insertIndex, mediaItem) currentPlaylist.add(insertIndex, mediaItem)
val origCurrent = currentPlaylist.getOrNull(currentIndex)?.let { c -> originalPlaylist.indexOfFirst { it.url == c.url } } ?: -1
originalPlaylist.add((origCurrent + 1).coerceIn(0, originalPlaylist.size), mediaItem)
urlIndexMap.clear() urlIndexMap.clear()
currentPlaylist.forEachIndexed { index, item -> currentPlaylist.forEachIndexed { index, item ->
urlIndexMap[item.url] = index urlIndexMap[item.url] = index
@ -331,6 +365,7 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val index = urlIndexMap[mediaItem.url] ?: return@withContext val index = urlIndexMap[mediaItem.url] ?: return@withContext
currentPlaylist.removeAt(index) currentPlaylist.removeAt(index)
originalPlaylist.removeAll { it.url == mediaItem.url }
urlIndexMap.clear() urlIndexMap.clear()
currentPlaylist.forEachIndexed { i, item -> currentPlaylist.forEachIndexed { i, item ->
urlIndexMap[item.url] = i urlIndexMap[item.url] = i
@ -346,6 +381,9 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
val item = currentPlaylist.removeAt(fromIndex) val item = currentPlaylist.removeAt(fromIndex)
currentPlaylist.add(toIndex, item) currentPlaylist.add(toIndex, item)
if (!shuffleEnabled && fromIndex in originalPlaylist.indices && toIndex in originalPlaylist.indices) {
originalPlaylist.add(toIndex, originalPlaylist.removeAt(fromIndex))
}
urlIndexMap.clear() urlIndexMap.clear()
currentPlaylist.forEachIndexed { i, it -> currentPlaylist.forEachIndexed { i, it ->
urlIndexMap[it.url] = i urlIndexMap[it.url] = i
@ -398,6 +436,7 @@ actual class AudioPlayer actual constructor(context: Any) : AudioPlayerInterface
disposed = true disposed = true
exoPlayer.release() exoPlayer.release()
currentPlaylist.clear() currentPlaylist.clear()
originalPlaylist.clear()
urlIndexMap.clear() urlIndexMap.clear()
_playerState.tryEmit(PlayerState.IDLE) _playerState.tryEmit(PlayerState.IDLE)