android/tv: fix focus highlighting on back buttons

updates tailscale/corp#26199

The back buttons would not highlight properly when
they are focussed on Android TV.  This pulls in what
was implemented for the Avatar.

Some small tweaks to the action animation so that it
has a nice radius instead of a square box.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
pull/605/head
Jonathan Nobels 10 months ago
parent 0ed18a2b0a
commit aea5aa840b

@ -42,7 +42,7 @@ fun Avatar(
action: (() -> Unit)? = null, action: (() -> Unit)? = null,
isFocusable: Boolean = false isFocusable: Boolean = false
) { ) {
var isFocused = remember { mutableStateOf(false) } val isFocused = remember { mutableStateOf(false) }
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
// Outer Box for the larger focusable and clickable area // Outer Box for the larger focusable and clickable area

@ -4,6 +4,7 @@
package com.tailscale.ipn.ui.view package com.tailscale.ipn.ui.view
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
@ -11,6 +12,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.CheckCircle
@ -24,10 +26,14 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.ripple import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tailscale.ipn.ui.theme.topAppBar import com.tailscale.ipn.ui.theme.topAppBar
@ -77,23 +83,32 @@ fun Header(
@Composable @Composable
fun BackArrow(action: () -> Unit, focusRequester: FocusRequester) { fun BackArrow(action: () -> Unit, focusRequester: FocusRequester) {
val modifier = val isFocused = remember { mutableStateOf(false) }
val boxModifier =
if (isAndroidTV()) { if (isAndroidTV()) {
Modifier.focusRequester(focusRequester) Modifier.focusRequester(focusRequester)
.focusable() // Ensure the composable can receive focus .clip(CircleShape) // Ensure both the focus and click area are circular
.background(
if (isFocused.value) MaterialTheme.colorScheme.surface else Color.Transparent,
)
.onFocusChanged { focusState -> isFocused.value = focusState.isFocused }
.focusable()
} else { } else {
Modifier Modifier
} }
Box(modifier = modifier.padding(start = 8.dp, end = 8.dp)) { val iconModifier =
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(bounded = false, radius = 24.dp),
onClick = action)
Box(modifier = boxModifier.padding(start = 8.dp, end = 8.dp)) {
Icon( Icon(
Icons.AutoMirrored.Filled.ArrowBack, Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Go back to the previous screen", contentDescription = "Go back to the previous screen",
modifier = modifier = iconModifier)
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(bounded = true),
onClick = action))
} }
} }

Loading…
Cancel
Save