diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index b06b97f491..8d94ba9d2e 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -28,6 +28,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -100,36 +103,54 @@ fun MediaPlayerControllerView( contentColor = ElementTheme.colors.iconOnSolidPrimary, ) } + val a11yPause = stringResource(CommonStrings.a11y_pause) + val a11yPlay = stringResource(CommonStrings.a11y_play) IconButton( modifier = Modifier - .size(36.dp), + .size(36.dp) + .semantics { + stateDescription = if (state.isPlaying) a11yPause else a11yPlay + }, onClick = onTogglePlay, colors = colors, ) { if (state.isPlaying) { Icon( imageVector = CompoundIcons.PauseSolid(), - contentDescription = stringResource(CommonStrings.a11y_pause) + contentDescription = null, ) } else { Icon( imageVector = CompoundIcons.PlaySolid(), - contentDescription = stringResource(CommonStrings.a11y_play) + contentDescription = null, ) } } + val position = state.displayProgressInMillis.toHumanReadableDuration() + val a11yPosition = stringResource(CommonStrings.a11y_position, position) Text( modifier = Modifier .widthIn(min = 48.dp) - .padding(horizontal = 8.dp), - text = state.displayProgressInMillis.toHumanReadableDuration(), + .padding(horizontal = 8.dp) + .semantics { + contentDescription = a11yPosition + }, + text = position, textAlign = TextAlign.Center, color = ElementTheme.colors.textPrimary, style = ElementTheme.typography.fontBodyXsMedium, ) var lastSelectedValue by remember { mutableFloatStateOf(-1f) } Slider( - modifier = Modifier.weight(1f), + modifier = Modifier + .weight(1f) + .semantics { + // Speak out a progress percent instead of milliseconds + stateDescription = buildString { + append((state.progressAsFloat * 100).toInt()) + append("%") + } + }, valueRange = 0f..state.durationInMillis.toFloat(), value = lastSelectedValue.takeIf { it >= 0 } ?: state.seekingToMillis?.toFloat() @@ -146,10 +167,14 @@ fun MediaPlayerControllerView( val formattedDuration = remember(state.durationInMillis) { state.durationInMillis.toHumanReadableDuration() } + val a11yDuration = stringResource(CommonStrings.a11y_duration, formattedDuration) Text( modifier = Modifier .widthIn(min = 48.dp) - .padding(horizontal = 8.dp), + .padding(horizontal = 8.dp) + .semantics { + contentDescription = a11yDuration + }, text = formattedDuration, textAlign = TextAlign.Center, color = ElementTheme.colors.textPrimary, diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ee0f51000a..fd3679a140 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -9,6 +9,7 @@ "%1$d digit entered" "%1$d digits entered" + "Duration: %1$s" "Edit avatar" "The full address will be %1$s" "Encryption details" @@ -33,6 +34,7 @@ "Playback speed" "Poll" "Ended poll" + "Position: %1$s" "QR Code" "React with %1$s" "React with other emojis"