fix focus for android tv

This commit is contained in:
2026-06-23 19:53:43 +03:00
parent ebc2ed7dc9
commit 16a78e8f6f
9 changed files with 124 additions and 68 deletions
@@ -42,11 +42,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusProperties
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
@@ -144,10 +143,7 @@ fun DefaultControls(
}
},
modifier = Modifier.fillMaxWidth()
.focusableWithBorder(focus.slider) {
next = if (audioTracksState.isNotEmpty()) focus.audioTrack else focus.lock
previous = focus.next
}
.focusableWithBorder(focus.slider)
.onPreviewKeyEvent {
if (it.type == KeyEventType.KeyDown) {
when (it.key) {
@@ -179,10 +175,7 @@ private fun LockUiButton(
) {
IconButton(
onClick = { onChanged(!state) },
modifier = Modifier.focusableWithBorder(focusContainer.lock) {
next = focusContainer.prev
previous = if (hasAudioTrack) focusContainer.audioTrack else focusContainer.slider
}
modifier = Modifier.focusableWithBorder(focusContainer.lock)
) {
val icon = if (state) Res.drawable.lock_24px else Res.drawable.lock_open_24px
Icon(painterResource(icon), null, tint = Color.White)
@@ -202,10 +195,7 @@ private fun AudioTracksButton(
val tracks = tracks.map { track -> track to FocusRequester() }
IconButton(
onClick = { expanded = true },
modifier = Modifier.focusableWithBorder(focusContainer.audioTrack) {
next = focusContainer.lock
previous = focusContainer.slider
}
modifier = Modifier.focusableWithBorder(focusContainer.audioTrack)
) {
Icon(
painter = painterResource(Res.drawable.music_note_24px),
@@ -239,10 +229,7 @@ private fun AudioTracksButton(
expanded = false
onSelect(track.id)
},
modifier = Modifier.focusableWithBorder(requester) {
previous = tracks.getOrNull(i - 1)?.second ?: focusContainer.audioTrack
next = tracks.getOrNull(i + 1)?.second ?: focusContainer.audioTrack
}
modifier = Modifier.focusableWithBorder(requester)
)
}
}
@@ -311,10 +298,7 @@ private fun BoxScope.CenterControls(
controller.seekTo(0)
controller.play()
}
}, modifier = Modifier.focusableWithBorder(focusContainer.next) {
previous = focusContainer.lock
next = focusContainer.slider
}
}, modifier = Modifier.focusableWithBorder(focusContainer.next)
) {
Icon(
painter = painterResource(Res.drawable.replay_24px),
@@ -333,10 +317,7 @@ private fun BoxScope.CenterControls(
IconButton(
onClick = {
scope.launch { controller.seek(true) }
}, modifier = Modifier.focusableWithBorder(focusContainer.prev) {
next = focusContainer.next
previous = focusContainer.play
}
}, modifier = Modifier.focusableWithBorder(focusContainer.prev)
) {
Text(
text = "-$DEF_SEEK_TIME",
@@ -350,10 +331,7 @@ private fun BoxScope.CenterControls(
scope.launch {
if (state.isPlaying) controller.pause() else controller.play()
}
}, modifier = Modifier.focusableWithBorder(focusContainer.next) {
next = focusContainer.next
previous = focusContainer.prev
}
}, modifier = Modifier.focusableWithBorder(focusContainer.play)
) {
Icon(
painter = painterResource(
@@ -371,10 +349,7 @@ private fun BoxScope.CenterControls(
IconButton(
onClick = {
scope.launch { controller.seek(false) }
}, modifier = Modifier.focusableWithBorder(focusContainer.next) {
next = focusContainer.slider
previous = focusContainer.play
}
}, modifier = Modifier.focusableWithBorder(focusContainer.next)
) {
Text(
text = "+$DEF_SEEK_TIME",
@@ -383,9 +358,8 @@ private fun BoxScope.CenterControls(
)
}
LaunchedEffect(null) {
delay(1_000)
val result = focusContainer.play.requestFocus()
println("DefaultControls focus changed? $result")
withFrameNanos {}
focusContainer.play.requestFocus()
}
}
}
@@ -434,18 +408,14 @@ private class FocusContainer(
)
@Composable
private fun Modifier.focusableWithBorder(
requester: FocusRequester,
focusProperties: FocusProperties.() -> Unit
): Modifier {
private fun Modifier.focusableWithBorder(requester: FocusRequester): Modifier {
var isFocused by remember { mutableStateOf(false) }
return focusRequester(requester)
.focusProperties(focusProperties)
return this.focusRequester(requester)
.onFocusChanged { isFocused = it.isFocused }
.border(
width = 2.dp,
color = if (isFocused) {
MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
MaterialTheme.colorScheme.primary
} else {
Color.Transparent
},