new: add playback state handling across platforms and integrate main play action in PlayerScreen
This commit is contained in:
@@ -1,26 +1,35 @@
|
||||
package fr.ajaury.gwenedeg.player.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import fr.ajaury.gwenedeg.player.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerUiState
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerViewModel
|
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
||||
@OptIn(ExperimentalResourceApi::class)
|
||||
@Composable
|
||||
fun PlayerScreen(
|
||||
recordTitle: String,
|
||||
viewModel: PlayerViewModel = koinViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
viewModel.play(filePath = "files/demat.ogg")
|
||||
onDispose {
|
||||
@@ -28,6 +37,19 @@ fun PlayerScreen(
|
||||
}
|
||||
}
|
||||
|
||||
PlayerScreen(
|
||||
recordTitle = recordTitle,
|
||||
uiState = uiState,
|
||||
onMainPlayActionButtonClicked = viewModel::performMainPlayAction,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlayerScreen(
|
||||
recordTitle: String,
|
||||
uiState: PlayerUiState,
|
||||
onMainPlayActionButtonClicked: () -> Unit,
|
||||
) {
|
||||
Scaffold { innerPadding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -35,12 +57,62 @@ fun PlayerScreen(
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Playing: $recordTitle",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
)
|
||||
|
||||
Button(onClick = onMainPlayActionButtonClicked) {
|
||||
Text(text = uiState.playbackState.toActionLabel())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PlaybackState.toActionLabel(): String =
|
||||
when (this) {
|
||||
PlaybackState.PLAYING -> "Pause"
|
||||
PlaybackState.ENDED -> "Replay"
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> "Play"
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PlayerScreenPlayingPreview() {
|
||||
GwenedegTheme {
|
||||
PlayerScreen(
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(playbackState = PlaybackState.PLAYING),
|
||||
onMainPlayActionButtonClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PlayerScreenPausedPreview() {
|
||||
GwenedegTheme {
|
||||
PlayerScreen(
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(playbackState = PlaybackState.PAUSED),
|
||||
onMainPlayActionButtonClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PlayerScreenEndedPreview() {
|
||||
GwenedegTheme {
|
||||
PlayerScreen(
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(playbackState = PlaybackState.ENDED),
|
||||
onMainPlayActionButtonClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package fr.ajaury.gwenedeg.player.ui.viewmodel
|
||||
|
||||
import fr.ajaury.gwenedeg.player.PlaybackState
|
||||
|
||||
data class PlayerUiState(
|
||||
val playbackState: PlaybackState = PlaybackState.IDLE,
|
||||
)
|
||||
+25
-2
@@ -1,25 +1,48 @@
|
||||
package fr.ajaury.gwenedeg.player.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
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.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class PlayerViewModel(
|
||||
private val audioPlayer: AudioPlayer,
|
||||
private val audioSessionManager: AudioSessionManager,
|
||||
) : 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(),
|
||||
)
|
||||
|
||||
init {
|
||||
audioSessionManager.activate()
|
||||
}
|
||||
|
||||
fun play(filePath: String) {
|
||||
this.filePath = filePath
|
||||
audioPlayer.load(uri = Res.getUri(filePath))
|
||||
audioPlayer.play()
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
audioPlayer.pause()
|
||||
fun performMainPlayAction() {
|
||||
when (uiState.value.playbackState) {
|
||||
PlaybackState.PLAYING -> audioPlayer.pause()
|
||||
PlaybackState.ENDED -> filePath?.let { play(it) }
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> audioPlayer.play()
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
|
||||
Reference in New Issue
Block a user