diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/BetterShuffleOrder.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/BetterShuffleOrder.kt
similarity index 99%
rename from app/src/main/java/org/oxycblt/auxio/playback/service/BetterShuffleOrder.kt
rename to app/src/main/java/org/oxycblt/auxio/playback/player/BetterShuffleOrder.kt
index 0f037a75a..5b1a5ad04 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/service/BetterShuffleOrder.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/player/BetterShuffleOrder.kt
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.playback.service
+package org.oxycblt.auxio.playback.player
import androidx.media3.common.C
import androidx.media3.exoplayer.source.ShuffleOrder
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/GaplessPlayerKernel.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt
similarity index 76%
rename from app/src/main/java/org/oxycblt/auxio/playback/service/GaplessPlayerKernel.kt
rename to app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt
index d0fbefd4f..d532ad2a7 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/service/GaplessPlayerKernel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/player/GaplessQueuer.kt
@@ -1,30 +1,17 @@
-package org.oxycblt.auxio.playback.service
+package org.oxycblt.auxio.playback.player
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
-import org.oxycblt.auxio.playback.PlaybackSettings
-class GaplessPlayerKernel(private val exoPlayer: ExoPlayer, private val playbackSettings: PlaybackSettings) : PlayerKernel, PlaybackSettings.Listener {
- init {
- playbackSettings.registerListener(this)
+class GaplessQueuer private constructor(private val exoPlayer: ExoPlayer) : Queuer {
+ data object Factory : Queuer.Factory {
+ override fun create(exoPlayer: ExoPlayer) = GaplessQueuer(exoPlayer)
}
-
- override val isPlaying: Boolean = exoPlayer.isPlaying
- override var playWhenReady: Boolean = exoPlayer.playWhenReady
- set(value) {
- field = value
- exoPlayer.playWhenReady = value
- }
- override val currentPosition: Long = exoPlayer.currentPosition
- @get:Player.RepeatMode override var repeatMode: Int = exoPlayer.repeatMode
- set(value) {
- field = value
- exoPlayer.repeatMode = value
- updatePauseOnRepeat()
- }
- override val audioSessionId: Int = exoPlayer.audioSessionId
+ override val currentMediaItem: MediaItem? = exoPlayer.currentMediaItem
+ override val currentMediaItemIndex: Int = exoPlayer.currentMediaItemIndex
+ override val shuffleModeEnabled: Boolean = exoPlayer.shuffleModeEnabled
override fun computeHeap(): List {
return (0 until exoPlayer.mediaItemCount).map { exoPlayer.getMediaItemAt(it) }
@@ -72,20 +59,6 @@ class GaplessPlayerKernel(private val exoPlayer: ExoPlayer, private val playback
override fun computeFirstMediaItemIndex() =
exoPlayer.currentTimeline.getFirstWindowIndex(exoPlayer.shuffleModeEnabled)
- override fun addListener(player: Player.Listener) = exoPlayer.addListener(player)
- override fun removeListener(player: Player.Listener) = exoPlayer.removeListener(player)
- override fun release() {
- exoPlayer.release()
- playbackSettings.unregisterListener(this)
- }
-
- override val currentMediaItem: MediaItem? = exoPlayer.currentMediaItem
- override val currentMediaItemIndex: Int = exoPlayer.currentMediaItemIndex
- override val shuffleModeEnabled: Boolean = exoPlayer.shuffleModeEnabled
-
- override fun play() = exoPlayer.play()
- override fun pause() = exoPlayer.pause()
- override fun seekTo(positionMs: Long) = exoPlayer.seekTo(positionMs)
override fun goto(mediaItemIndex: Int) = exoPlayer.seekTo(mediaItemIndex, C.TIME_UNSET)
override fun seekToNext() = exoPlayer.seekToNext()
@@ -164,18 +137,9 @@ class GaplessPlayerKernel(private val exoPlayer: ExoPlayer, private val playback
if (exoPlayer.shuffleModeEnabled) {
// Have to manually refresh the shuffle seed and anchor it to the new current songs
exoPlayer.setShuffleOrder(
- BetterShuffleOrder(exoPlayer.mediaItemCount, exoPlayer.currentMediaItemIndex))
+ BetterShuffleOrder(exoPlayer.mediaItemCount, exoPlayer.currentMediaItemIndex)
+ )
}
}
-
- override fun onPauseOnRepeatChanged() {
- super.onPauseOnRepeatChanged()
- updatePauseOnRepeat()
- }
-
- private fun updatePauseOnRepeat() {
- exoPlayer.pauseAtEndOfMediaItems =
- exoPlayer.repeatMode == Player.REPEAT_MODE_ONE && playbackSettings.pauseOnRepeat
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerFactory.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerFactory.kt
new file mode 100644
index 000000000..baecc5d4b
--- /dev/null
+++ b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerFactory.kt
@@ -0,0 +1,115 @@
+package org.oxycblt.auxio.playback.player
+
+import android.content.Context
+import androidx.media3.common.AudioAttributes
+import androidx.media3.common.C
+import androidx.media3.common.Player
+import androidx.media3.common.Player.RepeatMode
+import androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.exoplayer.RenderersFactory
+import androidx.media3.exoplayer.audio.AudioCapabilities
+import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer
+import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
+import androidx.media3.exoplayer.source.MediaSource
+import org.oxycblt.auxio.playback.replaygain.ReplayGainAudioProcessor
+import javax.inject.Inject
+
+interface PlayerFactory {
+ fun create(context: Context): ThinPlayer
+
+}
+
+interface ThinPlayer {
+ val isPlaying: Boolean
+ var playWhenReady: Boolean
+ val currentPosition: Long
+ @get:RepeatMode var repeatMode: Int
+ val audioSessionId: Int
+ var pauseAtEndOfMediaItems: Boolean
+
+ fun attach(listener: Player.Listener)
+ fun release()
+
+ fun play()
+ fun pause()
+ fun seekTo(positionMs: Long)
+
+ fun intoQueuer(queuerFactory: Queuer.Factory): Queuer
+}
+
+class PlayerFactoryImpl(@Inject private val mediaSourceFactory: MediaSource.Factory, @Inject private val replayGainProcessor: ReplayGainAudioProcessor) : PlayerFactory {
+ override fun create(context: Context): ThinPlayer {
+ // Since Auxio is a music player, only specify an audio renderer to save
+ // battery/apk size/cache size
+ val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ ->
+ arrayOf(
+ FfmpegAudioRenderer(handler, audioListener, replayGainProcessor),
+ MediaCodecAudioRenderer(
+ context,
+ MediaCodecSelector.DEFAULT,
+ handler,
+ audioListener,
+ AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
+ replayGainProcessor))
+ }
+
+ val exoPlayer =
+ ExoPlayer.Builder(context, audioRenderer)
+ .setMediaSourceFactory(mediaSourceFactory)
+ // Enable automatic WakeLock support
+ .setWakeMode(C.WAKE_MODE_LOCAL)
+ .setAudioAttributes(
+ // Signal that we are a music player.
+ AudioAttributes.Builder()
+ .setUsage(C.USAGE_MEDIA)
+ .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
+ .build(),
+ true)
+ .build()
+
+ return ThinPlayerImpl(exoPlayer, replayGainProcessor)
+ }
+}
+
+private class ThinPlayerImpl(
+ private val exoPlayer: ExoPlayer,
+ private val replayGainProcessor: ReplayGainAudioProcessor
+) : ThinPlayer {
+ override val isPlaying: Boolean get() = exoPlayer.isPlaying
+ override var playWhenReady: Boolean
+ get() = exoPlayer.playWhenReady
+ set(value) {
+ exoPlayer.playWhenReady = value
+ }
+ override val currentPosition: Long get() = exoPlayer.currentPosition
+ override var repeatMode: Int
+ get() = exoPlayer.repeatMode
+ set(value) {
+ exoPlayer.repeatMode = value
+ }
+ override val audioSessionId: Int get() = exoPlayer.audioSessionId
+ override var pauseAtEndOfMediaItems: Boolean
+ get() = exoPlayer.pauseAtEndOfMediaItems
+ set(value) {
+ exoPlayer.pauseAtEndOfMediaItems = value
+ }
+
+ override fun attach(listener: Player.Listener) {
+ exoPlayer.addListener(listener)
+ replayGainProcessor.attach()
+ }
+
+ override fun release() {
+ replayGainProcessor.release()
+ exoPlayer.release()
+ }
+
+ override fun play() = exoPlayer.play()
+
+ override fun pause() = exoPlayer.pause()
+
+ override fun seekTo(positionMs: Long) = exoPlayer.seekTo(positionMs)
+
+ override fun intoQueuer(queuerFactory: Queuer.Factory) = queuerFactory.create(exoPlayer)
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt
similarity index 79%
rename from app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt
rename to app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt
index bb51708b7..ae254f713 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/service/ExoPlaybackStateHolder.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/player/PlayerStateHolder.kt
@@ -16,23 +16,14 @@
* along with this program. If not, see .
*/
-package org.oxycblt.auxio.playback.service
+package org.oxycblt.auxio.playback.player
import android.content.Context
import android.content.Intent
import android.media.audiofx.AudioEffect
-import androidx.media3.common.AudioAttributes
-import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
-import androidx.media3.decoder.ffmpeg.FfmpegAudioRenderer
-import androidx.media3.exoplayer.ExoPlayer
-import androidx.media3.exoplayer.RenderersFactory
-import androidx.media3.exoplayer.audio.AudioCapabilities
-import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer
-import androidx.media3.exoplayer.mediacodec.MediaCodecSelector
-import androidx.media3.exoplayer.source.MediaSource
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -61,9 +52,9 @@ import org.oxycblt.auxio.playback.state.StateAck
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
-class ExoPlaybackStateHolder(
+class PlayerStateHolder(
private val context: Context,
- private val kernel: PlayerKernel,
+ private val playerFactory: PlayerFactory,
private val playbackManager: PlaybackStateManager,
private val persistenceRepository: PersistenceRepository,
private val playbackSettings: PlaybackSettings,
@@ -75,52 +66,24 @@ class ExoPlaybackStateHolder(
PlaybackStateHolder,
Player.Listener,
MusicRepository.UpdateListener,
- ImageSettings.Listener {
+ ImageSettings.Listener,
+ PlaybackSettings.Listener {
class Factory
@Inject
constructor(
- @ApplicationContext private val context: Context,
private val playbackManager: PlaybackStateManager,
private val persistenceRepository: PersistenceRepository,
private val playbackSettings: PlaybackSettings,
+ private val playerFactory: PlayerFactory,
private val commandFactory: PlaybackCommand.Factory,
- private val mediaSourceFactory: MediaSource.Factory,
private val replayGainProcessor: ReplayGainAudioProcessor,
private val musicRepository: MusicRepository,
private val imageSettings: ImageSettings,
) {
- fun create(): ExoPlaybackStateHolder {
- // Since Auxio is a music player, only specify an audio renderer to save
- // battery/apk size/cache size
- val audioRenderer = RenderersFactory { handler, _, audioListener, _, _ ->
- arrayOf(
- FfmpegAudioRenderer(handler, audioListener, replayGainProcessor),
- MediaCodecAudioRenderer(
- context,
- MediaCodecSelector.DEFAULT,
- handler,
- audioListener,
- AudioCapabilities.DEFAULT_AUDIO_CAPABILITIES,
- replayGainProcessor))
- }
-
- val exoPlayer =
- ExoPlayer.Builder(context, audioRenderer)
- .setMediaSourceFactory(mediaSourceFactory)
- // Enable automatic WakeLock support
- .setWakeMode(C.WAKE_MODE_LOCAL)
- .setAudioAttributes(
- // Signal that we are a music player.
- AudioAttributes.Builder()
- .setUsage(C.USAGE_MEDIA)
- .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
- .build(),
- true)
- .build()
-
- return ExoPlaybackStateHolder(
+ fun create(context: Context): PlayerStateHolder {
+ return PlayerStateHolder(
context,
- GaplessPlayerKernel(exoPlayer, playbackSettings),
+ playerFactory,
playbackManager,
persistenceRepository,
playbackSettings,
@@ -136,25 +99,29 @@ class ExoPlaybackStateHolder(
private val restoreScope = CoroutineScope(Dispatchers.IO + saveJob)
private var currentSaveJob: Job? = null
private var openAudioEffectSession = false
+ private val player = playerFactory.create(context)
+ private val queuer = player.intoQueuer(GaplessQueuer.Factory)
var sessionOngoing = false
private set
fun attach() {
+ player.attach(this)
+ playbackSettings.registerListener(this)
imageSettings.registerListener(this)
- kernel.addListener(this)
playbackManager.registerStateHolder(this)
musicRepository.addUpdateListener(this)
}
fun release() {
saveJob.cancel()
- kernel.removeListener(this)
+ player.release()
+ playbackSettings.unregisterListener(this)
playbackManager.unregisterStateHolder(this)
musicRepository.removeUpdateListener(this)
replayGainProcessor.release()
imageSettings.unregisterListener(this)
- kernel.release()
+ player.release()
}
override var parent: MusicParent? = null
@@ -162,15 +129,15 @@ class ExoPlaybackStateHolder(
override val progression: Progression
get() {
- val mediaItem = kernel.currentMediaItem ?: return Progression.nil()
+ val mediaItem = queuer.currentMediaItem ?: return Progression.nil()
val duration = mediaItem.mediaMetadata.extras?.getLong("durationMs") ?: Long.MAX_VALUE
- val clampedPosition = kernel.currentPosition.coerceAtLeast(0).coerceAtMost(duration)
- return Progression.from(kernel.playWhenReady, kernel.isPlaying, clampedPosition)
+ val clampedPosition = player.currentPosition.coerceAtLeast(0).coerceAtMost(duration)
+ return Progression.from(player.playWhenReady, player.isPlaying, clampedPosition)
}
override val repeatMode
get() =
- when (val repeatMode = kernel.repeatMode) {
+ when (val repeatMode = player.repeatMode) {
Player.REPEAT_MODE_OFF -> RepeatMode.NONE
Player.REPEAT_MODE_ONE -> RepeatMode.TRACK
Player.REPEAT_MODE_ALL -> RepeatMode.ALL
@@ -178,12 +145,12 @@ class ExoPlaybackStateHolder(
}
override val audioSessionId: Int
- get() = kernel.audioSessionId
+ get() = player.audioSessionId
override fun resolveQueue(): RawQueue {
- val heap = kernel.computeHeap()
- val shuffledMapping = if (kernel.shuffleModeEnabled) kernel.computeMapping() else emptyList()
- return RawQueue(heap.mapNotNull { it.song }, shuffledMapping, kernel.currentMediaItemIndex)
+ val heap = queuer.computeHeap()
+ val shuffledMapping = if (queuer.shuffleModeEnabled) queuer.computeMapping() else emptyList()
+ return RawQueue(heap.mapNotNull { it.song }, shuffledMapping, queuer.currentMediaItemIndex)
}
override fun handleDeferred(action: DeferredPlayback): Boolean {
@@ -236,22 +203,23 @@ class ExoPlaybackStateHolder(
}
override fun playing(playing: Boolean) {
- kernel.playWhenReady = playing
+ player.playWhenReady = playing
}
override fun seekTo(positionMs: Long) {
- kernel.seekTo(positionMs)
+ player.seekTo(positionMs)
deferSave()
// Ack handled w/ExoPlayer events
}
override fun repeatMode(repeatMode: RepeatMode) {
- kernel.repeatMode =
+ player.repeatMode =
when (repeatMode) {
RepeatMode.NONE -> Player.REPEAT_MODE_OFF
RepeatMode.ALL -> Player.REPEAT_MODE_ALL
RepeatMode.TRACK -> Player.REPEAT_MODE_ONE
}
+ updatePauseOnRepeat()
playbackManager.ack(this, StateAck.RepeatModeChanged)
deferSave()
}
@@ -263,14 +231,14 @@ class ExoPlaybackStateHolder(
command.song
?.let { command.queue.indexOf(it) }
.also { check(it != -1) { "Start song not in queue" } }
- kernel.prepareNew(mediaItems, startIndex, command.shuffled)
- kernel.play()
+ queuer.prepareNew(mediaItems, startIndex, command.shuffled)
+ player.play()
playbackManager.ack(this, StateAck.NewPlayback)
deferSave()
}
override fun shuffled(shuffled: Boolean) {
- kernel.shuffled(shuffled)
+ queuer.shuffled(shuffled)
playbackManager.ack(this, StateAck.QueueReordered)
deferSave()
}
@@ -279,17 +247,17 @@ class ExoPlaybackStateHolder(
// Replicate the old pseudo-circular queue behavior when no repeat option is implemented.
// Basically, you can't skip back and wrap around the queue, but you can skip forward and
// wrap around the queue, albeit playback will be paused.
- if (kernel.repeatMode == Player.REPEAT_MODE_ALL || kernel.hasNextMediaItem()) {
- kernel.seekToNext()
+ if (player.repeatMode == Player.REPEAT_MODE_ALL || queuer.hasNextMediaItem()) {
+ queuer.seekToNext()
if (!playbackSettings.rememberPause) {
- kernel.play()
+ player.play()
}
} else {
- kernel.goto(kernel.computeFirstMediaItemIndex())
+ queuer.goto(queuer.computeFirstMediaItemIndex())
// TODO: Dislike the UX implications of this, I feel should I bite the bullet
// and switch to dynamic skip enable/disable?
if (!playbackSettings.rememberPause) {
- kernel.pause()
+ player.pause()
}
}
playbackManager.ack(this, StateAck.IndexMoved)
@@ -298,47 +266,47 @@ class ExoPlaybackStateHolder(
override fun prev() {
if (playbackSettings.rewindWithPrev) {
- kernel.seekToPrevious()
- } else if (kernel.hasPreviousMediaItem()) {
- kernel.seekToPreviousMediaItem()
+ queuer.seekToPrevious()
+ } else if (queuer.hasPreviousMediaItem()) {
+ queuer.seekToPreviousMediaItem()
} else {
- kernel.seekTo(0)
+ player.seekTo(0)
}
if (!playbackSettings.rememberPause) {
- kernel.play()
+ player.play()
}
playbackManager.ack(this, StateAck.IndexMoved)
deferSave()
}
override fun goto(index: Int) {
- val indices = kernel.computeMapping()
+ val indices = queuer.computeMapping()
if (indices.isEmpty()) {
return
}
val trueIndex = indices[index]
- kernel.goto(trueIndex)
+ queuer.goto(trueIndex)
if (!playbackSettings.rememberPause) {
- kernel.play()
+ player.play()
}
playbackManager.ack(this, StateAck.IndexMoved)
deferSave()
}
override fun playNext(songs: List, ack: StateAck.PlayNext) {
- kernel.addBottomMediaItems(songs.map { it.buildMediaItem() })
+ queuer.addBottomMediaItems(songs.map { it.buildMediaItem() })
playbackManager.ack(this, ack)
deferSave()
}
override fun addToQueue(songs: List, ack: StateAck.AddToQueue) {
- kernel.addTopMediaItems(songs.map { it.buildMediaItem() })
+ queuer.addTopMediaItems(songs.map { it.buildMediaItem() })
playbackManager.ack(this, ack)
deferSave()
}
override fun move(from: Int, to: Int, ack: StateAck.Move) {
- val indices = kernel.computeMapping()
+ val indices = queuer.computeMapping()
if (indices.isEmpty()) {
return
}
@@ -346,22 +314,22 @@ class ExoPlaybackStateHolder(
val trueFrom = indices[from]
val trueTo = indices[to]
- kernel.moveMediaItem(trueFrom, trueTo)
+ queuer.moveMediaItem(trueFrom, trueTo)
playbackManager.ack(this, ack)
deferSave()
}
override fun remove(at: Int, ack: StateAck.Remove) {
- val indices = kernel.computeMapping()
+ val indices = queuer.computeMapping()
if (indices.isEmpty()) {
return
}
val trueIndex = indices[at]
- val songWillChange = kernel.currentMediaItemIndex == trueIndex
- kernel.removeMediaItem(trueIndex)
+ val songWillChange = queuer.currentMediaItemIndex == trueIndex
+ queuer.removeMediaItem(trueIndex)
if (songWillChange && !playbackSettings.rememberPause) {
- kernel.play()
+ player.play()
}
playbackManager.ack(this, ack)
deferSave()
@@ -379,8 +347,8 @@ class ExoPlaybackStateHolder(
sendEvent = true
}
if (rawQueue != resolveQueue()) {
- kernel.prepareSaved(rawQueue.heap.map { it.buildMediaItem() }, rawQueue.shuffledMapping, rawQueue.heapIndex, rawQueue.isShuffled)
- kernel.pause()
+ queuer.prepareSaved(rawQueue.heap.map { it.buildMediaItem() }, rawQueue.shuffledMapping, rawQueue.heapIndex, rawQueue.isShuffled)
+ player.pause()
sendEvent = true
}
if (sendEvent) {
@@ -403,7 +371,7 @@ class ExoPlaybackStateHolder(
}
override fun reset(ack: StateAck.NewPlayback) {
- kernel.discard()
+ queuer.discard()
playbackManager.ack(this, ack)
deferSave()
}
@@ -413,7 +381,7 @@ class ExoPlaybackStateHolder(
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
- if (kernel.playWhenReady) {
+ if (player.playWhenReady) {
// Mark that we have started playing so that the notification can now be posted.
logD("Player has started playing")
sessionOngoing = true
@@ -435,9 +403,9 @@ class ExoPlaybackStateHolder(
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
- if (playbackState == Player.STATE_ENDED && kernel.repeatMode == Player.REPEAT_MODE_OFF) {
+ if (playbackState == Player.STATE_ENDED && player.repeatMode == Player.REPEAT_MODE_OFF) {
goto(0)
- kernel.pause()
+ player.pause()
}
}
@@ -490,6 +458,20 @@ class ExoPlaybackStateHolder(
}
}
+ // --- PLAYBACK SETTINGS METHODS ---
+
+ override fun onPauseOnRepeatChanged() {
+ super.onPauseOnRepeatChanged()
+ updatePauseOnRepeat()
+ }
+
+ private fun updatePauseOnRepeat() {
+ player.pauseAtEndOfMediaItems =
+ player.repeatMode == Player.REPEAT_MODE_ONE && playbackSettings.pauseOnRepeat
+ }
+
+ // --- OVERRIDES ---
+
private fun save(cb: () -> Unit) {
saveJob {
persistenceRepository.saveState(playbackManager.toSavedState())
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/PlayerKernel.kt b/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt
similarity index 60%
rename from app/src/main/java/org/oxycblt/auxio/playback/service/PlayerKernel.kt
rename to app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt
index 1615088b3..3274ba34b 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/service/PlayerKernel.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/player/Queuer.kt
@@ -1,29 +1,13 @@
-package org.oxycblt.auxio.playback.service
+package org.oxycblt.auxio.playback.player
import androidx.media3.common.MediaItem
-import androidx.media3.common.Player
-import org.oxycblt.auxio.music.Song
-import org.oxycblt.auxio.playback.state.RawQueue
-import org.oxycblt.auxio.playback.state.RepeatMode
+import androidx.media3.exoplayer.ExoPlayer
-interface PlayerKernel {
- // REPLICAS
- val isPlaying: Boolean
- var playWhenReady: Boolean
- val currentPosition: Long
- @get:Player.RepeatMode var repeatMode: Int
- val audioSessionId: Int
+interface Queuer {
val currentMediaItem: MediaItem?
val currentMediaItemIndex: Int
val shuffleModeEnabled: Boolean
- fun addListener(player: Player.Listener)
- fun removeListener(player: Player.Listener)
- fun release()
-
- fun play()
- fun pause()
- fun seekTo(positionMs: Long)
fun goto(mediaItemIndex: Int)
fun seekToNext()
@@ -47,5 +31,8 @@ interface PlayerKernel {
fun addTopMediaItems(mediaItems: List)
fun addBottomMediaItems(mediaItems: List)
fun shuffled(shuffled: Boolean)
-}
+ interface Factory {
+ fun create(exoPlayer: ExoPlayer): Queuer
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/oxycblt/auxio/playback/service/PlaybackServiceFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/service/PlaybackServiceFragment.kt
index 04af2a40f..c588b7264 100644
--- a/app/src/main/java/org/oxycblt/auxio/playback/service/PlaybackServiceFragment.kt
+++ b/app/src/main/java/org/oxycblt/auxio/playback/service/PlaybackServiceFragment.kt
@@ -25,6 +25,7 @@ import kotlinx.coroutines.Job
import org.oxycblt.auxio.ForegroundListener
import org.oxycblt.auxio.ForegroundServiceNotification
import org.oxycblt.auxio.IntegerTable
+import org.oxycblt.auxio.playback.player.PlayerStateHolder
import org.oxycblt.auxio.playback.state.DeferredPlayback
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.util.logD
@@ -35,7 +36,7 @@ private constructor(
private val context: Context,
private val foregroundListener: ForegroundListener,
private val playbackManager: PlaybackStateManager,
- exoHolderFactory: ExoPlaybackStateHolder.Factory,
+ playerHolderFactory: PlayerStateHolder.Factory,
sessionHolderFactory: MediaSessionHolder.Factory,
widgetComponentFactory: WidgetComponent.Factory,
systemReceiverFactory: SystemPlaybackReceiver.Factory,
@@ -44,7 +45,7 @@ private constructor(
@Inject
constructor(
private val playbackManager: PlaybackStateManager,
- private val exoHolderFactory: ExoPlaybackStateHolder.Factory,
+ private val exoHolderFactory: PlayerStateHolder.Factory,
private val sessionHolderFactory: MediaSessionHolder.Factory,
private val widgetComponentFactory: WidgetComponent.Factory,
private val systemReceiverFactory: SystemPlaybackReceiver.Factory,
@@ -61,7 +62,7 @@ private constructor(
}
private val waitJob = Job()
- private val exoHolder = exoHolderFactory.create()
+ private val exoHolder = playerHolderFactory.create(context)
private val sessionHolder = sessionHolderFactory.create(context, foregroundListener)
private val widgetComponent = widgetComponentFactory.create(context)
private val systemReceiver = systemReceiverFactory.create(context, widgetComponent)