refactor: set traversal order for player
This commit is contained in:
@@ -58,6 +58,7 @@ fun PlayerControl(
|
|||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
SpeedPreferenceButton(
|
SpeedPreferenceButton(
|
||||||
|
modifier = Modifier.traversalOrder(PlayerTraversalOrder.PLAYBACK_SETTINGS),
|
||||||
isActive = isPlaybackModified,
|
isActive = isPlaybackModified,
|
||||||
onSpeedClicked = onSpeedClicked,
|
onSpeedClicked = onSpeedClicked,
|
||||||
)
|
)
|
||||||
@@ -69,17 +70,20 @@ fun PlayerControl(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
PreviousButton(
|
PreviousButton(
|
||||||
|
modifier = Modifier.traversalOrder(PlayerTraversalOrder.PREVIOUS),
|
||||||
canGoToPreviousSentence = canGoToPreviousSentence,
|
canGoToPreviousSentence = canGoToPreviousSentence,
|
||||||
onPreviousSentenceClicked = onPreviousSentenceClicked,
|
onPreviousSentenceClicked = onPreviousSentenceClicked,
|
||||||
)
|
)
|
||||||
|
|
||||||
PlayButton(
|
PlayButton(
|
||||||
|
modifier = Modifier.traversalOrder(PlayerTraversalOrder.PLAY),
|
||||||
playbackState = playbackState,
|
playbackState = playbackState,
|
||||||
breakDuration = breakBetweenPhrases,
|
breakDuration = breakBetweenPhrases,
|
||||||
onMainPlayActionButtonClicked = onMainPlayActionButtonClicked,
|
onMainPlayActionButtonClicked = onMainPlayActionButtonClicked,
|
||||||
)
|
)
|
||||||
|
|
||||||
NextButton(
|
NextButton(
|
||||||
|
modifier = Modifier.traversalOrder(PlayerTraversalOrder.NEXT),
|
||||||
canGoToNextSentence = canGoToNextSentence,
|
canGoToNextSentence = canGoToNextSentence,
|
||||||
onNextSentenceClicked = onNextSentenceClicked,
|
onNextSentenceClicked = onNextSentenceClicked,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
import androidx.compose.ui.semantics.contentDescription
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.isTraversalGroup
|
||||||
import androidx.compose.ui.semantics.onClick
|
import androidx.compose.ui.semantics.onClick
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -116,6 +117,9 @@ fun PlayerScreen(
|
|||||||
var showSpeed by remember { mutableStateOf(false) }
|
var showSpeed by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
// Root of the screen reader order defined by PlayerTraversalOrder, which interleaves the
|
||||||
|
// top bar and the controls.
|
||||||
|
modifier = Modifier.semantics { isTraversalGroup = true },
|
||||||
topBar = {
|
topBar = {
|
||||||
PlayerTopBar(
|
PlayerTopBar(
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
@@ -191,7 +195,11 @@ private fun PlayerTopBar(
|
|||||||
showPreferences: () -> Unit,
|
showPreferences: () -> Unit,
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
modifier = Modifier.shadow(elevation = 2.dp),
|
modifier = Modifier
|
||||||
|
.shadow(elevation = 2.dp)
|
||||||
|
// TopAppBar makes itself a traversal group, which would keep its subtitle settings
|
||||||
|
// button next to the title. Flattened so PlayerTraversalOrder can move it last.
|
||||||
|
.semantics { isTraversalGroup = false },
|
||||||
title = {
|
title = {
|
||||||
// Animate the title in as it loads, and between records: the record data isn't there on
|
// 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.
|
// the first frame, so fade + rise it into place once uiState carries it.
|
||||||
@@ -204,7 +212,9 @@ private fun PlayerTopBar(
|
|||||||
label = "PlayerTitle",
|
label = "PlayerTitle",
|
||||||
) { (emoji, title) ->
|
) { (emoji, title) ->
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.clearAndSetSemantics {
|
modifier = Modifier
|
||||||
|
.traversalOrder(PlayerTraversalOrder.TITLE)
|
||||||
|
.clearAndSetSemantics {
|
||||||
contentDescription = title.translation ?: title.transcription
|
contentDescription = title.translation ?: title.transcription
|
||||||
},
|
},
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -242,7 +252,9 @@ private fun PlayerTopBar(
|
|||||||
val openSettingsLabel = stringResource(Res.string.settings_open_action_description)
|
val openSettingsLabel = stringResource(Res.string.settings_open_action_description)
|
||||||
IconButton(
|
IconButton(
|
||||||
// Names the action: "double-tap to open settings" rather than the generic "to activate".
|
// Names the action: "double-tap to open settings" rather than the generic "to activate".
|
||||||
modifier = Modifier.semantics {
|
modifier = Modifier
|
||||||
|
.traversalOrder(PlayerTraversalOrder.SUBTITLE_SETTINGS)
|
||||||
|
.semantics {
|
||||||
onClick(label = openSettingsLabel) {
|
onClick(label = openSettingsLabel) {
|
||||||
showPreferences()
|
showPreferences()
|
||||||
true
|
true
|
||||||
@@ -265,6 +277,7 @@ private fun BackButton(onBackClicked: () -> Unit) {
|
|||||||
IconButton(
|
IconButton(
|
||||||
onClick = onBackClicked,
|
onClick = onBackClicked,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.traversalOrder(PlayerTraversalOrder.BACK)
|
||||||
.semantics {
|
.semantics {
|
||||||
onClick(label = backActionLabel) {
|
onClick(label = backActionLabel) {
|
||||||
onBackClicked()
|
onBackClicked()
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package bzh.ajaury.chombev.player.ui
|
||||||
|
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.isTraversalGroup
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.semantics.traversalIndex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen reader order on the player, which departs from the visual layout: the title and back
|
||||||
|
* button first, then the transport controls, the settings last.
|
||||||
|
*/
|
||||||
|
internal enum class PlayerTraversalOrder {
|
||||||
|
TITLE,
|
||||||
|
BACK,
|
||||||
|
PLAY,
|
||||||
|
PREVIOUS,
|
||||||
|
NEXT,
|
||||||
|
PLAYBACK_SETTINGS,
|
||||||
|
SUBTITLE_SETTINGS,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places this element at [order] in the player's screen reader order. Grouped so that an element
|
||||||
|
* made of several nodes (e.g. the play button and its break ring) moves as a whole.
|
||||||
|
*/
|
||||||
|
internal fun Modifier.traversalOrder(order: PlayerTraversalOrder): Modifier =
|
||||||
|
semantics {
|
||||||
|
isTraversalGroup = true
|
||||||
|
traversalIndex = order.ordinal.toFloat()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user