new: add support for record title translations

This commit is contained in:
2026-06-26 14:39:20 +02:00
parent 184610fa20
commit d4e5990a5f
5 changed files with 46 additions and 18 deletions
@@ -72,12 +72,23 @@ fun PlayerScreen(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
Text( Column {
text = uiState.recordTitle, Text(
style = MaterialTheme.typography.headlineSmall, text = uiState.recordTitle,
maxLines = 1, style = MaterialTheme.typography.headlineSmall,
overflow = TextOverflow.Ellipsis, maxLines = 1,
) overflow = TextOverflow.Ellipsis,
)
uiState.recordTitleTranslation?.let { translation ->
Text(
text = translation,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}, },
navigationIcon = { navigationIcon = {
IconButton(onClick = onBackClicked) { IconButton(onClick = onBackClicked) {
@@ -132,6 +143,7 @@ private fun PlayerScreenPlayingPreview() {
PlayerScreen( PlayerScreen(
uiState = PlayerUiState( uiState = PlayerUiState(
recordTitle = "Demat", recordTitle = "Demat",
recordTitleTranslation = "Bonjour",
playbackState = PlaybackState.PLAYING, playbackState = PlaybackState.PLAYING,
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000), playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
subtitleLines = previewSubtitleLines, subtitleLines = previewSubtitleLines,
@@ -151,6 +163,7 @@ private fun PlayerScreenPausedPreview() {
PlayerScreen( PlayerScreen(
uiState = PlayerUiState( uiState = PlayerUiState(
recordTitle = "Demat", recordTitle = "Demat",
recordTitleTranslation = "Bonjour",
playbackState = PlaybackState.PAUSED, playbackState = PlaybackState.PAUSED,
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000), playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
subtitleLines = previewSubtitleLines, subtitleLines = previewSubtitleLines,
@@ -170,6 +183,7 @@ private fun PlayerScreenEndedPreview() {
PlayerScreen( PlayerScreen(
uiState = PlayerUiState( uiState = PlayerUiState(
recordTitle = "Demat", recordTitle = "Demat",
recordTitleTranslation = "Bonjour",
playbackState = PlaybackState.ENDED, playbackState = PlaybackState.ENDED,
playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000), playbackTiming = PlaybackTiming(positionMs = 1234, durationMs = 5000),
subtitleLines = previewSubtitleLines, subtitleLines = previewSubtitleLines,
@@ -6,6 +6,7 @@ import fr.ajaury.gwenedeg.subtitle.domain.SubtitleLine
data class PlayerUiState( data class PlayerUiState(
val recordTitle: String = "", val recordTitle: String = "",
val recordTitleTranslation: String? = null,
val playbackState: PlaybackState = PlaybackState.IDLE, val playbackState: PlaybackState = PlaybackState.IDLE,
val playbackTiming: PlaybackTiming = PlaybackTiming(), val playbackTiming: PlaybackTiming = PlaybackTiming(),
val subtitleLines: List<SubtitleLine> = emptyList(), val subtitleLines: List<SubtitleLine> = emptyList(),
@@ -6,7 +6,7 @@ import fr.ajaury.gwenedeg.core.logging.domain.Logger
import fr.ajaury.gwenedeg.player.domain.AudioSessionManager import fr.ajaury.gwenedeg.player.domain.AudioSessionManager
import fr.ajaury.gwenedeg.player.domain.PlaybackRepository import fr.ajaury.gwenedeg.player.domain.PlaybackRepository
import fr.ajaury.gwenedeg.player.model.PlaybackState import fr.ajaury.gwenedeg.player.model.PlaybackState
import fr.ajaury.gwenedeg.player.model.PlaybackTiming import fr.ajaury.gwenedeg.records.domain.Record
import fr.ajaury.gwenedeg.records.domain.RecordRepository import fr.ajaury.gwenedeg.records.domain.RecordRepository
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -26,26 +26,26 @@ class PlayerViewModel(
private val audioSessionManager: AudioSessionManager, private val audioSessionManager: AudioSessionManager,
private val logger: Logger, private val logger: Logger,
) : ViewModel() { ) : ViewModel() {
private val recordTitle = MutableStateFlow("") private val record = MutableStateFlow<Record?>(null)
private var recordLoaded = false private var recordLoaded = false
val uiState: StateFlow<PlayerUiState> = combine( val uiState: StateFlow<PlayerUiState> = combine(
recordTitle, record,
playbackRepository.playbackState, playbackRepository.playbackState,
playbackRepository.playbackTiming, playbackRepository.playbackTiming,
playbackRepository.subtitle, playbackRepository.subtitle,
playbackRepository.currentSubtitleIndex, playbackRepository.currentSubtitleIndex,
) { recordTitle, playbackState, playbackTiming, subtitle, currentSubtitleIndex -> ) { record, playbackState, playbackTiming, subtitle, currentSubtitleIndex ->
PlayerUiState( PlayerUiState(
recordTitle = recordTitle, recordTitle = record?.title.orEmpty(),
recordTitleTranslation = record?.titleTranslation,
playbackState = playbackState, playbackState = playbackState,
playbackTiming = playbackTiming, playbackTiming = playbackTiming,
subtitleLines = subtitle.lines, subtitleLines = subtitle.lines,
currentSubtitleIndex = currentSubtitleIndex, currentSubtitleIndex = currentSubtitleIndex,
) )
} }.onStart { loadRecord() }
.onStart { loadRecord() }
.stateIn( .stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5.seconds), started = SharingStarted.WhileSubscribed(5.seconds),
@@ -65,14 +65,14 @@ class PlayerViewModel(
recordLoaded = true recordLoaded = true
viewModelScope.launch { viewModelScope.launch {
val record = recordRepository.getRecord(id = recordId) val loadedRecord = recordRepository.getRecord(id = recordId)
if (record == null) { if (loadedRecord == null) {
logger.error(message = "Record not found: $recordId") logger.error(message = "Record not found: $recordId")
return@launch return@launch
} }
recordTitle.value = record.title record.value = loadedRecord
playbackRepository.load(record = record) playbackRepository.load(record = loadedRecord)
} }
} }
@@ -4,6 +4,7 @@ data class Record(
val id: Int, val id: Int,
val index: Int, val index: Int,
val title: String, val title: String,
val titleTranslation: String? = null,
val audioResourcePath: String, val audioResourcePath: String,
val subtitleResourcePath: String, val subtitleResourcePath: String,
val translationSubtitleResourcePath: String? = null, val translationSubtitleResourcePath: String? = null,
@@ -2,9 +2,11 @@ package fr.ajaury.gwenedeg.records.ui
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -32,7 +34,16 @@ fun RecordView(
text = record.index.toString().padStart(2), text = record.index.toString().padStart(2),
fontFamily = FontFamily.Monospace, fontFamily = FontFamily.Monospace,
) )
Text(text = record.title) Column {
Text(text = record.title)
record.titleTranslation?.let { translation ->
Text(
text = translation,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
} }
} }
@@ -45,6 +56,7 @@ private fun RecordViewPreview() {
id = 1, id = 1,
index = 1, index = 1,
title = "Sample Record", title = "Sample Record",
titleTranslation = "Exemple d'enregistrement",
audioResourcePath = "files/Chom_bev_01.ogg", audioResourcePath = "files/Chom_bev_01.ogg",
subtitleResourcePath = "files/Chom_bev_01_st_bzh.lrc", subtitleResourcePath = "files/Chom_bev_01_st_bzh.lrc",
), ),