new: display current playback position and implement cross-platform position tracking

This commit is contained in:
2026-06-19 12:23:51 +02:00
parent bc848ebb62
commit 090305dbde
8 changed files with 76 additions and 10 deletions
@@ -97,6 +97,11 @@ fun PlayerScreen(
style = MaterialTheme.typography.headlineMedium,
)
Text(
text = "${uiState.positionMs} ms",
style = MaterialTheme.typography.bodyMedium,
)
Button(
modifier = Modifier.size(60.dp),
contentPadding = PaddingValues(0.dp),
@@ -132,7 +137,7 @@ private fun PlayerScreenPlayingPreview() {
GwenedegTheme {
PlayerScreen(
recordTitle = "Demat",
uiState = PlayerUiState(playbackState = PlaybackState.PLAYING),
uiState = PlayerUiState(playbackState = PlaybackState.PLAYING, positionMs = 1234),
onMainPlayActionButtonClicked = {},
onBackClicked = {},
)
@@ -145,7 +150,7 @@ private fun PlayerScreenPausedPreview() {
GwenedegTheme {
PlayerScreen(
recordTitle = "Demat",
uiState = PlayerUiState(playbackState = PlaybackState.PAUSED),
uiState = PlayerUiState(playbackState = PlaybackState.PAUSED, positionMs = 1234),
onMainPlayActionButtonClicked = {},
onBackClicked = {},
)
@@ -158,7 +163,7 @@ private fun PlayerScreenEndedPreview() {
GwenedegTheme {
PlayerScreen(
recordTitle = "Demat",
uiState = PlayerUiState(playbackState = PlaybackState.ENDED),
uiState = PlayerUiState(playbackState = PlaybackState.ENDED, positionMs = 1234),
onMainPlayActionButtonClicked = {},
onBackClicked = {},
)
@@ -4,4 +4,5 @@ import fr.ajaury.gwenedeg.player.PlaybackState
data class PlayerUiState(
val playbackState: PlaybackState = PlaybackState.IDLE,
val positionMs: Long = 0L,
)
@@ -7,11 +7,17 @@ import fr.ajaury.gwenedeg.player.AudioPlayer
import fr.ajaury.gwenedeg.player.AudioSessionManager
import fr.ajaury.gwenedeg.player.PlaybackState
import gwenedeg.shared.generated.resources.Res
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
class PlayerViewModel(
@@ -21,13 +27,36 @@ class PlayerViewModel(
) : ViewModel() {
private var filePath: String? = null
val uiState: StateFlow<PlayerUiState> = audioPlayer.playbackState
.map { PlayerUiState(playbackState = it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5.seconds),
initialValue = PlayerUiState(),
private val playbackState = audioPlayer.playbackState
@OptIn(ExperimentalCoroutinesApi::class)
private val currentPosition = playbackState
.flatMapLatest { state ->
if (state == PlaybackState.PLAYING) {
flow {
while (true) {
emit(audioPlayer.currentPosition())
delay(POSITION_POLL_INTERVAL)
}
}
} else {
flowOf(audioPlayer.currentPosition())
}
}
val uiState: StateFlow<PlayerUiState> = combine(
playbackState,
currentPosition,
) { playbackState, currentPositionMs ->
PlayerUiState(
playbackState = playbackState,
positionMs = currentPositionMs.inWholeMilliseconds,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5.seconds),
initialValue = PlayerUiState(),
)
init {
audioSessionManager.activate()
@@ -60,4 +89,8 @@ class PlayerViewModel(
audioSessionManager.deactivate()
audioPlayer.release()
}
companion object {
private val POSITION_POLL_INTERVAL = 200.milliseconds
}
}