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)