refactor: improve accessibility title for player
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package bzh.ajaury.chombev
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import bzh.ajaury.chombev.core.model.Phrase
|
||||
import bzh.ajaury.chombev.core.model.Record
|
||||
import bzh.ajaury.chombev.resources.generated.resources.Res
|
||||
import bzh.ajaury.chombev.resources.generated.resources.records_chapter_number
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
internal fun rowDescription(record: Record): String = rowDescription(index = record.index, title = record.title)
|
||||
|
||||
@Composable
|
||||
internal fun rowDescription(
|
||||
index: Int,
|
||||
title: Phrase,
|
||||
): String {
|
||||
val chapterNumber = stringResource(Res.string.records_chapter_number, index)
|
||||
val rowDescription =
|
||||
listOfNotNull(chapterNumber, title.translation)
|
||||
.joinToString(separator = ", ")
|
||||
return rowDescription
|
||||
}
|
||||
+30
-22
@@ -40,6 +40,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import bzh.ajaury.chombev.core.model.Phrase
|
||||
import bzh.ajaury.chombev.core.model.Record
|
||||
import bzh.ajaury.chombev.player.model.PlaybackState
|
||||
import bzh.ajaury.chombev.player.model.PlaybackTiming
|
||||
import bzh.ajaury.chombev.player.model.PlayerState
|
||||
@@ -51,6 +52,7 @@ import bzh.ajaury.chombev.resources.generated.resources.player_back
|
||||
import bzh.ajaury.chombev.resources.generated.resources.player_back_action_label
|
||||
import bzh.ajaury.chombev.resources.generated.resources.settings_open_action_description
|
||||
import bzh.ajaury.chombev.resources.generated.resources.subtitle_preferences_action
|
||||
import bzh.ajaury.chombev.rowDescription
|
||||
import bzh.ajaury.chombev.subtitle.model.SubtitleLine
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
@@ -204,35 +206,38 @@ private fun PlayerTopBar(
|
||||
// Animate the title in as it loads, and between records: the record data isn't there on
|
||||
// the first frame, so fade + rise it into place once uiState carries it.
|
||||
AnimatedContent(
|
||||
targetState = uiState.recordEmoji to uiState.recordTitle,
|
||||
targetState = uiState.record,
|
||||
transitionSpec = {
|
||||
fadeIn(tween()) togetherWith
|
||||
fadeOut(tween())
|
||||
},
|
||||
label = "PlayerTitle",
|
||||
) { (emoji, title) ->
|
||||
) { record ->
|
||||
// Same description as the record's row in the list; none until the record is loaded.
|
||||
val description =
|
||||
record?.index?.let { rowDescription(index = it, title = record.title) }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.traversalOrder(PlayerTraversalOrder.TITLE)
|
||||
.clearAndSetSemantics {
|
||||
contentDescription = title.translation ?: title.transcription
|
||||
description?.let { contentDescription = it }
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = emoji,
|
||||
text = record?.emoji.orEmpty(),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = title.transcription,
|
||||
text = record?.title?.transcription.orEmpty(),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
title.translation?.let { translation ->
|
||||
record?.title?.translation?.let { translation ->
|
||||
Text(
|
||||
text = translation,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
@@ -317,17 +322,25 @@ private val previewSubtitleLines =
|
||||
),
|
||||
)
|
||||
|
||||
private val fakeRecord = Record(
|
||||
id = 1,
|
||||
index = 1,
|
||||
emoji = "🙏",
|
||||
title = Phrase(
|
||||
transcription = "Demat",
|
||||
translation = "Bonjour",
|
||||
),
|
||||
audioResourcePath = "",
|
||||
subtitleResourcePath = "",
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PlayerScreenPlayingPreview() {
|
||||
MaterialTheme {
|
||||
PlayerScreen(
|
||||
uiState = PlayerUiState(
|
||||
recordEmoji = "🙏",
|
||||
recordTitle = Phrase(
|
||||
transcription = "Demat",
|
||||
translation = "Bonjour",
|
||||
),
|
||||
fakeRecord,
|
||||
playerState = PlayerState(
|
||||
playbackState = PlaybackState.PLAYING,
|
||||
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||
@@ -348,14 +361,13 @@ private fun PlayerScreenPausedPreview() {
|
||||
MaterialTheme {
|
||||
PlayerScreen(
|
||||
uiState = PlayerUiState(
|
||||
recordEmoji = "🙏",
|
||||
recordTitle = Phrase(
|
||||
transcription = "Demat",
|
||||
translation = "Bonjour",
|
||||
),
|
||||
record = fakeRecord,
|
||||
playerState = PlayerState(
|
||||
playbackState = PlaybackState.PAUSED,
|
||||
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||
playbackTiming = PlaybackTiming(
|
||||
position = 1.2.seconds,
|
||||
duration = 5.seconds,
|
||||
),
|
||||
subtitleLines = previewSubtitleLines,
|
||||
currentSubtitleIndex = 1,
|
||||
),
|
||||
@@ -373,11 +385,7 @@ private fun PlayerScreenEndedPreview() {
|
||||
MaterialTheme {
|
||||
PlayerScreen(
|
||||
uiState = PlayerUiState(
|
||||
recordEmoji = "🙏",
|
||||
recordTitle = Phrase(
|
||||
transcription = "Demat",
|
||||
translation = "Bonjour",
|
||||
),
|
||||
record = fakeRecord,
|
||||
playerState = PlayerState(
|
||||
playbackState = PlaybackState.ENDED,
|
||||
playbackTiming = PlaybackTiming(position = 1.2.seconds, duration = 5.seconds),
|
||||
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
package bzh.ajaury.chombev.player.ui.viewmodel
|
||||
|
||||
import bzh.ajaury.chombev.core.model.Phrase
|
||||
import bzh.ajaury.chombev.core.model.Record
|
||||
import bzh.ajaury.chombev.player.model.PlayerState
|
||||
import bzh.ajaury.chombev.preferences.model.PlaybackPreferences
|
||||
import bzh.ajaury.chombev.preferences.model.SubtitlePreferences
|
||||
@@ -8,8 +8,7 @@ import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
data class PlayerUiState(
|
||||
val recordEmoji: String = "",
|
||||
val recordTitle: Phrase = Phrase(transcription = ""),
|
||||
val record: Record? = null,
|
||||
val playerState: PlayerState = PlayerState(),
|
||||
val subtitlePreferences: SubtitlePreferences = SubtitlePreferences(),
|
||||
val playbackSpeed: Float = PlaybackPreferences.DEFAULT_SPEED,
|
||||
@@ -20,7 +19,10 @@ data class PlayerUiState(
|
||||
get() = playerState.playbackTiming.position > 2.seconds
|
||||
|
||||
val canGoToNextSentence: Boolean
|
||||
get() = (playerState.currentSubtitleIndex ?: Int.MAX_VALUE) < playerState.subtitleLines.lastIndex
|
||||
get() = (
|
||||
playerState.currentSubtitleIndex
|
||||
?: Int.MAX_VALUE
|
||||
) < playerState.subtitleLines.lastIndex
|
||||
|
||||
/** True when any playback setting differs from its default, used to highlight the button. */
|
||||
val isPlaybackModified: Boolean
|
||||
|
||||
+1
-3
@@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import bzh.ajaury.chombev.core.accessibility.domain.AccessibilityRepository
|
||||
import bzh.ajaury.chombev.core.logging.domain.Logger
|
||||
import bzh.ajaury.chombev.core.model.Phrase
|
||||
import bzh.ajaury.chombev.core.model.Record
|
||||
import bzh.ajaury.chombev.player.domain.AudioSessionManager
|
||||
import bzh.ajaury.chombev.player.domain.PlaybackRepository
|
||||
@@ -42,8 +41,7 @@ class PlayerViewModel(
|
||||
preferencesRepository.playbackPreferences,
|
||||
) { record, playerState, subtitlePreferences, playbackPreferences ->
|
||||
PlayerUiState(
|
||||
recordEmoji = record?.emoji ?: "",
|
||||
recordTitle = record?.title ?: Phrase(transcription = ""),
|
||||
record = record,
|
||||
playerState = playerState,
|
||||
subtitlePreferences = subtitlePreferences,
|
||||
playbackSpeed = playbackPreferences.speed,
|
||||
|
||||
@@ -21,7 +21,7 @@ import bzh.ajaury.chombev.core.model.Phrase
|
||||
import bzh.ajaury.chombev.core.model.Record
|
||||
import bzh.ajaury.chombev.resources.generated.resources.Res
|
||||
import bzh.ajaury.chombev.resources.generated.resources.records_chapter_action_description
|
||||
import bzh.ajaury.chombev.resources.generated.resources.records_chapter_number
|
||||
import bzh.ajaury.chombev.rowDescription
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
@Composable
|
||||
@@ -29,10 +29,7 @@ fun RecordView(
|
||||
record: Record,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
val chapterNumber = stringResource(Res.string.records_chapter_number, record.index)
|
||||
val rowDescription =
|
||||
listOfNotNull(chapterNumber, record.title.transcription, record.title.translation)
|
||||
.joinToString(separator = ", ")
|
||||
val rowDescription = rowDescription(record)
|
||||
|
||||
Row(
|
||||
modifier =
|
||||
|
||||
Reference in New Issue
Block a user