@@ -31,6 +31,8 @@ import coil.annotation.ExperimentalCoilApi
31
31
import coil.compose.AsyncImage
32
32
import com.tailscale.ipn.R
33
33
import com.tailscale.ipn.ui.model.IpnLocal
34
+ import com.tailscale.ipn.ui.util.AndroidTVUtil
35
+ import com.tailscale.ipn.ui.util.conditional
34
36
35
37
@OptIn(ExperimentalCoilApi ::class )
36
38
@Composable
@@ -43,53 +45,49 @@ fun Avatar(
43
45
var isFocused = remember { mutableStateOf(false ) }
44
46
val focusManager = LocalFocusManager .current
45
47
46
- // Outer Box for the larger focusable and clickable area
47
- Box (
48
- contentAlignment = Alignment .Center ,
49
- modifier = Modifier
50
- .padding(4 .dp)
51
- .size((size * 1.5f ).dp) // Focusable area is larger than the avatar
52
- .clip(CircleShape ) // Ensure both the focus and click area are circular
53
- .background(
54
- if (isFocused.value) MaterialTheme .colorScheme.surface
55
- else Color .Transparent ,
56
- )
57
- .onFocusChanged { focusState ->
58
- isFocused.value = focusState.isFocused
59
- }
60
- .focusable() // Make this outer Box focusable (after onFocusChanged)
61
- .clickable(
62
- interactionSource = remember { MutableInteractionSource () },
63
- indication = ripple(bounded = true ), // Apply ripple effect inside circular bounds
64
- onClick = {
65
- action?.invoke()
48
+ // Outer Box for the larger focusable and clickable area
49
+ Box (
50
+ contentAlignment = Alignment .Center ,
51
+ modifier =
52
+ Modifier .conditional(AndroidTVUtil .isAndroidTV(), { padding(4 .dp) })
53
+ .conditional(
54
+ AndroidTVUtil .isAndroidTV(),
55
+ {
56
+ size((size * 1.5f ).dp) // Focusable area is larger than the avatar
57
+ })
58
+ .clip(CircleShape ) // Ensure both the focus and click area are circular
59
+ .background(
60
+ if (isFocused.value) MaterialTheme .colorScheme.surface else Color .Transparent ,
61
+ )
62
+ .onFocusChanged { focusState -> isFocused.value = focusState.isFocused }
63
+ .focusable() // Make this outer Box focusable (after onFocusChanged)
64
+ .clickable(
65
+ interactionSource = remember { MutableInteractionSource () },
66
+ indication = ripple(bounded = true ), // Apply ripple effect inside circular bounds
67
+ onClick = {
68
+ action?.invoke()
66
69
focusManager.clearFocus() // Clear focus after clicking the avatar
67
- }
68
- )
69
- ) {
70
+ })) {
70
71
// Inner Box to hold the avatar content (Icon or AsyncImage)
71
72
Box (
72
73
contentAlignment = Alignment .Center ,
73
- modifier = Modifier
74
- .size(size.dp)
75
- .clip(CircleShape )
76
- ) {
77
- // Always display the default icon as a background layer
78
- Icon (
79
- imageVector = Icons .Default .Person ,
80
- contentDescription = stringResource(R .string.settings_title),
81
- modifier =
82
- Modifier .size((size * 0.8f ).dp)
83
- .clip(CircleShape ) // Icon size slightly smaller than the Box
84
- )
74
+ modifier = Modifier .size(size.dp).clip(CircleShape )) {
75
+ // Always display the default icon as a background layer
76
+ Icon (
77
+ imageVector = Icons .Default .Person ,
78
+ contentDescription = stringResource(R .string.settings_title),
79
+ modifier =
80
+ Modifier .conditional(AndroidTVUtil .isAndroidTV(), { size((size * 0.8f ).dp) })
81
+ .clip(CircleShape ) // Icon size slightly smaller than the Box
82
+ )
85
83
86
- // Overlay the profile picture if available
87
- profile?.UserProfile ?.ProfilePicURL ?.let { url ->
88
- AsyncImage (
89
- model = url,
90
- modifier = Modifier .size(size.dp).clip(CircleShape ),
91
- contentDescription = null )
92
- }
93
- }
94
- }
84
+ // Overlay the profile picture if available
85
+ profile?.UserProfile ?.ProfilePicURL ?.let { url ->
86
+ AsyncImage (
87
+ model = url,
88
+ modifier = Modifier .size(size.dp).clip(CircleShape ),
89
+ contentDescription = null )
90
+ }
91
+ }
92
+ }
95
93
}
0 commit comments