Renderer.kt
Each line, with the release that last changed it.
| 0.5.0 | 1 | package dev.stave.domain.renderer |
| 2 | ||
| 3 | import dev.stave.domain.model.DidlItem | |
| 4 | import kotlinx.coroutines.flow.StateFlow | |
| 5 | import kotlin.time.Duration | |
| 6 | ||
| 7 | /** | |
| 8 | * The thing that actually plays audio. v0.0.1 ships [dev.stave.playback.LocalRenderer] only. | |
| 9 | * Cast (play flavor) and UPnP AV/OpenHome plug in behind this interface without UI changes. | |
| 10 | */ | |
| 11 | interface Renderer { | |
| 12 | val state: StateFlow<PlaybackState> | |
| 13 | ||
| 14 | suspend fun load(tracks: List<DidlItem.Track>, startIndex: Int = 0, autoplay: Boolean = true) | |
| 15 | /** Append [tracks] to the end of the queue. If the queue is empty, they become the queue | |
| 16 | * but are NOT auto-started — the user explicitly asked to queue, not to play. */ | |
| 17 | suspend fun enqueue(tracks: List<DidlItem.Track>) | |
| 18 | /** Insert [tracks] immediately after the currently-playing track. If the queue is empty, | |
| 19 | * the tracks become the queue AND playback begins. */ | |
| 20 | suspend fun playNext(tracks: List<DidlItem.Track>) | |
| 21 | /** Jump directly to queue position [index] and start playing. Out-of-range is a no-op. */ | |
| 22 | suspend fun skipToQueueItem(index: Int) | |
| 23 | /** Remove queue item at [index]. Removing the current item advances to the next; removing | |
| 24 | * the last remaining item stops playback. */ | |
| 25 | suspend fun removeFromQueue(index: Int) | |
| 26 | /** Reorder the queue by moving the item at [from] to position [to]. */ | |
| 27 | suspend fun moveInQueue(from: Int, to: Int) | |
| 28 | /** Empty the queue and stop playback. */ | |
| 29 | suspend fun clearQueue() | |
| 30 | suspend fun play() | |
| 31 | suspend fun pause() | |
| 32 | suspend fun stop() | |
| 33 | suspend fun seekTo(position: Duration) | |
| 34 | suspend fun next() | |
| 35 | suspend fun previous() | |
| 36 | suspend fun setVolume(volumePercent: Int) | |
| 37 | /** Cycle repeat mode: OFF → ALL → ONE → OFF. */ | |
| 38 | suspend fun setRepeatMode(mode: RepeatMode) | |
| 39 | /** Shuffle toggle. Turning on saves the current queue order; turning off restores it. | |
| 40 | * Implementation is responsible for the save/restore — UI just flips the flag. */ | |
| 41 | suspend fun setShuffle(enabled: Boolean) | |
| 42 | } | |
| 43 | ||
| 44 | enum class RepeatMode { OFF, ALL, ONE } | |
| 45 | ||
| 46 | data class PlaybackState( | |
| 47 | val current: DidlItem.Track? = null, | |
| 48 | val queue: List<DidlItem.Track> = emptyList(), | |
| 49 | val queueIndex: Int = -1, | |
| 50 | val isPlaying: Boolean = false, | |
| 51 | val isBuffering: Boolean = false, | |
| 52 | /** True when the user has explicitly stopped (or nothing has ever been loaded). | |
| 53 | * Distinguishes from paused-mid-track. Used by the NowPlayingBar visibility rule: | |
| 54 | * the bar shows while playing OR paused, but hides while stopped even if the queue | |
| 55 | * still has tracks loaded. */ | |
| 56 | val isStopped: Boolean = true, | |
| 57 | val position: Duration = Duration.ZERO, | |
| 58 | val duration: Duration = Duration.ZERO, | |
| 59 | val volumePercent: Int = 100, | |
| 60 | val repeatMode: RepeatMode = RepeatMode.OFF, | |
| 61 | val shuffleEnabled: Boolean = false, | |
| 62 | val error: String? = null, | |
| 63 | ) |


