refactor: improve player package structure
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
||||
package fr.ajaury.gwenedeg.player.model
|
||||
|
||||
data class PlaybackTiming(
|
||||
val positionMs: Long = 0L,
|
||||
val durationMs: Long = 0L,
|
||||
) {
|
||||
val progress: Float
|
||||
get() = if (durationMs > 0) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package fr.ajaury.gwenedeg.player.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Replay
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
|
||||
@Composable
|
||||
fun PlayerControl(
|
||||
playbackState: PlaybackState,
|
||||
playbackTiming: PlaybackTiming,
|
||||
onSeek: (Float) -> Unit = {},
|
||||
onMainPlayActionButtonClicked: () -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
var seekProgress by remember { mutableStateOf<Float?>(null) }
|
||||
Slider(
|
||||
value = seekProgress ?: playbackTiming.progress,
|
||||
onValueChange = { seekProgress = it },
|
||||
onValueChangeFinished = {
|
||||
seekProgress?.let { fraction ->
|
||||
if (playbackTiming.durationMs > 0) {
|
||||
onSeek(fraction)
|
||||
}
|
||||
}
|
||||
seekProgress = null
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.size(60.dp),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
onClick = onMainPlayActionButtonClicked,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = playbackState.toActionIcon(),
|
||||
contentDescription = playbackState.toActionContentDescription(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PlaybackState.toActionIcon(): ImageVector =
|
||||
when (this) {
|
||||
PlaybackState.PLAYING -> Icons.Default.Pause
|
||||
PlaybackState.ENDED -> Icons.Default.Replay
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> Icons.Default.PlayArrow
|
||||
}
|
||||
|
||||
private fun PlaybackState.toActionContentDescription(): String =
|
||||
when (this) {
|
||||
PlaybackState.PLAYING -> "Pause"
|
||||
PlaybackState.ENDED -> "Replay"
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> "Play"
|
||||
}
|
||||
@@ -1,57 +1,33 @@
|
||||
package fr.ajaury.gwenedeg.player.ui
|
||||
|
||||
import androidx.compose.foundation.gestures.animateScrollBy
|
||||
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Replay
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerUiState
|
||||
import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerViewModel
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import kotlin.math.abs
|
||||
|
||||
@Composable
|
||||
fun PlayerScreen(
|
||||
@@ -127,8 +103,9 @@ fun PlayerScreen(
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
PlayerState(
|
||||
uiState = uiState,
|
||||
PlayerControl(
|
||||
playbackState = uiState.playbackState,
|
||||
playbackTiming = uiState.playbackTiming,
|
||||
onSeek = onSeekToTimePart,
|
||||
onMainPlayActionButtonClicked = onMainPlayActionButtonClicked,
|
||||
)
|
||||
@@ -136,159 +113,6 @@ fun PlayerScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleList(
|
||||
lines: List<SubtitleLine>,
|
||||
currentIndex: Int?,
|
||||
onSeek: (position: Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
val isDragged by listState.interactionSource.collectIsDraggedAsState()
|
||||
|
||||
val centeredIndex by remember {
|
||||
derivedStateOf {
|
||||
val info = listState.layoutInfo
|
||||
if (info.visibleItemsInfo.isEmpty()) {
|
||||
-1
|
||||
} else {
|
||||
val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2f
|
||||
info.visibleItemsInfo.minByOrNull { abs((it.offset + it.size / 2f) - viewportCenter) }!!.index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-follow: re-center the playing line when it changes, unless the user is scrolling.
|
||||
LaunchedEffect(currentIndex) {
|
||||
if (currentIndex != null && !listState.isScrollInProgress) {
|
||||
listState.centerItem(currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// Snap to the centered line and seek to it once the user finishes scrolling.
|
||||
LaunchedEffect(listState, lines) {
|
||||
var wasDragged = false
|
||||
snapshotFlow { isDragged to listState.isScrollInProgress }
|
||||
.collect { (dragged, scrolling) ->
|
||||
if (dragged) {
|
||||
wasDragged = true
|
||||
} else if (wasDragged && !scrolling) {
|
||||
wasDragged = false
|
||||
val targetIndex = centeredIndex
|
||||
if (targetIndex in lines.indices) {
|
||||
listState.centerItem(targetIndex)
|
||||
onSeek(targetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
// Half-height padding so the first and last lines can reach the vertical center.
|
||||
contentPadding = PaddingValues(vertical = maxHeight / 2),
|
||||
) {
|
||||
itemsIndexed(lines) { index, line ->
|
||||
val isCurrent = index == centeredIndex
|
||||
Subtitle(
|
||||
line = line,
|
||||
isCurrent = isCurrent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun LazyListState.centerItem(index: Int) {
|
||||
val info = this.layoutInfo
|
||||
val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2
|
||||
val item = info.visibleItemsInfo.firstOrNull { it.index == index }
|
||||
if (item != null) {
|
||||
this.animateScrollBy((item.offset + item.size / 2 - viewportCenter).toFloat())
|
||||
} else {
|
||||
this.animateScrollToItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(
|
||||
line: SubtitleLine,
|
||||
isCurrent: Boolean,
|
||||
) {
|
||||
Text(
|
||||
text = line.text,
|
||||
style = if (isCurrent) {
|
||||
MaterialTheme.typography.headlineSmall
|
||||
} else {
|
||||
MaterialTheme.typography.bodyLarge
|
||||
},
|
||||
color = if (isCurrent) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PlayerState(
|
||||
uiState: PlayerUiState,
|
||||
onSeek: (Float) -> Unit,
|
||||
onMainPlayActionButtonClicked: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
var seekProgress by remember { mutableStateOf<Float?>(null) }
|
||||
Slider(
|
||||
value = seekProgress ?: uiState.progress,
|
||||
onValueChange = { seekProgress = it },
|
||||
onValueChangeFinished = {
|
||||
seekProgress?.let { fraction ->
|
||||
if (uiState.durationMs > 0) {
|
||||
onSeek(fraction)
|
||||
}
|
||||
}
|
||||
seekProgress = null
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Button(
|
||||
modifier = Modifier.size(60.dp),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
onClick = onMainPlayActionButtonClicked,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = uiState.playbackState.toActionIcon(),
|
||||
contentDescription = uiState.playbackState.toActionContentDescription(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PlaybackState.toActionIcon(): ImageVector =
|
||||
when (this) {
|
||||
PlaybackState.PLAYING -> Icons.Default.Pause
|
||||
PlaybackState.ENDED -> Icons.Default.Replay
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> Icons.Default.PlayArrow
|
||||
}
|
||||
|
||||
private fun PlaybackState.toActionContentDescription(): String =
|
||||
when (this) {
|
||||
PlaybackState.PLAYING -> "Pause"
|
||||
PlaybackState.ENDED -> "Replay"
|
||||
PlaybackState.PAUSED, PlaybackState.IDLE -> "Play"
|
||||
}
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !"),
|
||||
@@ -304,8 +128,7 @@ private fun PlayerScreenPlayingPreview() {
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(
|
||||
playbackState = PlaybackState.PLAYING,
|
||||
positionMs = 1234,
|
||||
durationMs = 5000,
|
||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
||||
subtitleLines = previewSubtitleLines,
|
||||
currentSubtitleIndex = 1,
|
||||
),
|
||||
@@ -324,8 +147,7 @@ private fun PlayerScreenPausedPreview() {
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(
|
||||
playbackState = PlaybackState.PAUSED,
|
||||
positionMs = 1234,
|
||||
durationMs = 5000,
|
||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
||||
subtitleLines = previewSubtitleLines,
|
||||
currentSubtitleIndex = 1,
|
||||
),
|
||||
@@ -344,8 +166,7 @@ private fun PlayerScreenEndedPreview() {
|
||||
recordTitle = "Demat",
|
||||
uiState = PlayerUiState(
|
||||
playbackState = PlaybackState.ENDED,
|
||||
positionMs = 1234,
|
||||
durationMs = 5000,
|
||||
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
|
||||
subtitleLines = previewSubtitleLines,
|
||||
currentSubtitleIndex = 2,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package fr.ajaury.gwenedeg.player.ui
|
||||
|
||||
import androidx.compose.foundation.gestures.animateScrollBy
|
||||
import androidx.compose.foundation.interaction.collectIsDraggedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
import fr.ajaury.gwenedeg.theme.GwenedegTheme
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
@Composable
|
||||
fun SubtitleList(
|
||||
modifier: Modifier = Modifier,
|
||||
lines: List<SubtitleLine>,
|
||||
currentIndex: Int?,
|
||||
onSeek: (position: Int) -> Unit = {},
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
val isDragged by listState.interactionSource.collectIsDraggedAsState()
|
||||
|
||||
val centeredIndex by remember {
|
||||
derivedStateOf {
|
||||
val info = listState.layoutInfo
|
||||
if (info.visibleItemsInfo.isEmpty()) {
|
||||
-1
|
||||
} else {
|
||||
val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2f
|
||||
info.visibleItemsInfo.minByOrNull { abs((it.offset + it.size / 2f) - viewportCenter) }!!.index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-follow: re-center the playing line when it changes, unless the user is scrolling.
|
||||
LaunchedEffect(currentIndex) {
|
||||
if (currentIndex != null && !listState.isScrollInProgress) {
|
||||
listState.centerItem(currentIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// Snap to the centered line and seek to it once the user finishes scrolling.
|
||||
LaunchedEffect(listState, lines) {
|
||||
var wasDragged = false
|
||||
snapshotFlow { isDragged to listState.isScrollInProgress }
|
||||
.collect { (dragged, scrolling) ->
|
||||
if (dragged) {
|
||||
wasDragged = true
|
||||
} else if (wasDragged && !scrolling) {
|
||||
wasDragged = false
|
||||
val targetIndex = centeredIndex
|
||||
if (targetIndex in lines.indices) {
|
||||
listState.centerItem(targetIndex)
|
||||
onSeek(targetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
// Half-height padding so the first and last lines can reach the vertical center.
|
||||
contentPadding = PaddingValues(vertical = maxHeight / 2),
|
||||
) {
|
||||
itemsIndexed(lines) { index, line ->
|
||||
val isCurrent = index == centeredIndex
|
||||
Subtitle(
|
||||
line = line,
|
||||
isCurrent = isCurrent,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun LazyListState.centerItem(index: Int) {
|
||||
val info = this.layoutInfo
|
||||
val viewportCenter = (info.viewportStartOffset + info.viewportEndOffset) / 2
|
||||
val item = info.visibleItemsInfo.firstOrNull { it.index == index }
|
||||
if (item != null) {
|
||||
this.animateScrollBy((item.offset + item.size / 2 - viewportCenter).toFloat())
|
||||
} else {
|
||||
this.animateScrollToItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Subtitle(
|
||||
line: SubtitleLine,
|
||||
isCurrent: Boolean,
|
||||
) {
|
||||
Text(
|
||||
text = line.text,
|
||||
style = if (isCurrent) {
|
||||
MaterialTheme.typography.headlineSmall
|
||||
} else {
|
||||
MaterialTheme.typography.bodyLarge
|
||||
},
|
||||
color = if (isCurrent) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
private val previewSubtitleLines =
|
||||
listOf(
|
||||
SubtitleLine(startMs = 0, text = "Demat deoc'h !"),
|
||||
SubtitleLine(startMs = 4310, text = "Kenavo !"),
|
||||
SubtitleLine(startMs = 7350, text = "Kenavo emberr !"),
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SubtitleListPreview() {
|
||||
GwenedegTheme {
|
||||
SubtitleList(
|
||||
lines = previewSubtitleLines,
|
||||
currentIndex = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -1,15 +1,12 @@
|
||||
package fr.ajaury.gwenedeg.player.ui.viewmodel
|
||||
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
|
||||
|
||||
data class PlayerUiState(
|
||||
val playbackState: PlaybackState = PlaybackState.IDLE,
|
||||
val positionMs: Long = 0L,
|
||||
val durationMs: Long = 0L,
|
||||
val playbackTiming: PlaybackTiming = PlaybackTiming(),
|
||||
val subtitleLines: List<SubtitleLine> = emptyList(),
|
||||
val currentSubtitleIndex: Int? = null,
|
||||
) {
|
||||
val progress: Float
|
||||
get() = if (durationMs > 0) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f
|
||||
}
|
||||
)
|
||||
|
||||
+6
-3
@@ -6,6 +6,7 @@ import fr.ajaury.gwenedeg.core.logging.domain.Logger
|
||||
import fr.ajaury.gwenedeg.player.domain.AudioPlayer
|
||||
import fr.ajaury.gwenedeg.player.domain.AudioSessionManager
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackTiming
|
||||
import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.GetCurrentSubtitleIndexUseCase
|
||||
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
|
||||
@@ -62,8 +63,10 @@ class PlayerViewModel(
|
||||
) { playbackState, currentPosition, subtitle ->
|
||||
PlayerUiState(
|
||||
playbackState = playbackState,
|
||||
positionMs = currentPosition.inWholeMilliseconds,
|
||||
durationMs = audioPlayer.duration.inWholeMilliseconds,
|
||||
playbackTiming = PlaybackTiming(
|
||||
positionMs = currentPosition.inWholeMilliseconds,
|
||||
durationMs = audioPlayer.duration.inWholeMilliseconds,
|
||||
),
|
||||
subtitleLines = subtitle.lines,
|
||||
currentSubtitleIndex = getCurrentSubtitleIndex(
|
||||
subtitle = subtitle,
|
||||
@@ -115,7 +118,7 @@ class PlayerViewModel(
|
||||
}
|
||||
|
||||
fun seekToTimePart(progress: Float) {
|
||||
val position = (progress.toDouble() * uiState.value.durationMs).milliseconds
|
||||
val position = (progress.toDouble() * uiState.value.playbackTiming.durationMs).milliseconds
|
||||
audioPlayer.seekTo(position)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user