android: fix avatar padding (#559)

Update Avatar to take isFocusable as a parameter, allowing us to make the avatar focusable in the main view but not in the settings / user switcher view. This fixes the issue where the padding is too big in the settings / user switcher view.

Fixes tailscale/corp#24370

Signed-off-by: kari-ts <kari@tailscale.com>
pull/550/head^2
kari-ts 1 year ago committed by GitHub
parent ba306bf883
commit bd745b5254
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -34,57 +34,52 @@ import com.tailscale.ipn.ui.model.IpnLocal
@OptIn(ExperimentalCoilApi::class) @OptIn(ExperimentalCoilApi::class)
@Composable @Composable
fun Avatar(profile: IpnLocal.LoginProfile?, size: Int = 50, action: (() -> Unit)? = null) { fun Avatar(
profile: IpnLocal.LoginProfile?,
size: Int = 50,
action: (() -> Unit)? = null,
isFocusable: Boolean = false
) {
var isFocused = remember { mutableStateOf(false) } var isFocused = remember { mutableStateOf(false) }
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
// Outer Box for the larger focusable and clickable area // Determine the modifier based on whether the avatar is focusable
Box( val outerModifier =
contentAlignment = Alignment.Center, Modifier.then(
modifier = Modifier if (isFocusable) {
.padding(4.dp) Modifier.padding(4.dp)
.size((size * 1.5f).dp) // Focusable area is larger than the avatar } else Modifier) // Add padding if focusable
.clip(CircleShape) // Ensure both the focus and click area are circular .size((size * 1.5f).dp)
.background( .clip(CircleShape)
if (isFocused.value) MaterialTheme.colorScheme.surface .background(if (isFocused.value) MaterialTheme.colorScheme.surface else Color.Transparent)
else Color.Transparent, .onFocusChanged { focusState -> isFocused.value = focusState.isFocused }
) .then(if (isFocusable) Modifier.focusable() else Modifier) // Conditionally add focusable
.onFocusChanged { focusState ->
isFocused.value = focusState.isFocused
}
.focusable() // Make this outer Box focusable (after onFocusChanged)
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = ripple(bounded = true), // Apply ripple effect inside circular bounds indication = ripple(bounded = true),
onClick = { onClick = {
action?.invoke() action?.invoke()
focusManager.clearFocus() // Clear focus after clicking the avatar focusManager.clearFocus() // Clear focus after clicking
} })
)
) { // Outer Box for the larger focusable and clickable area
Box(contentAlignment = Alignment.Center, modifier = outerModifier) {
// Inner Box to hold the avatar content (Icon or AsyncImage) // Inner Box to hold the avatar content (Icon or AsyncImage)
Box( Box(contentAlignment = Alignment.Center, modifier = Modifier.size(size.dp).clip(CircleShape)) {
contentAlignment = Alignment.Center,
modifier = Modifier
.size(size.dp)
.clip(CircleShape)
) {
if (profile?.UserProfile?.ProfilePicURL != null) { if (profile?.UserProfile?.ProfilePicURL != null) {
AsyncImage( AsyncImage(
model = profile.UserProfile.ProfilePicURL, model = profile.UserProfile.ProfilePicURL,
modifier = Modifier.size(size.dp).clip(CircleShape), modifier = Modifier.size(size.dp).clip(CircleShape),
contentDescription = null contentDescription = null)
)
} else { } else {
Icon( Icon(
imageVector = Icons.Default.Person, imageVector = Icons.Default.Person,
contentDescription = stringResource(R.string.settings_title), contentDescription = stringResource(R.string.settings_title),
modifier = Modifier modifier =
.size((size * 0.8f).dp) Modifier.size((size * 0.8f).dp)
.clip(CircleShape) // Icon size slightly smaller than the Box .clip(CircleShape) // Icon size slightly smaller than the Box
) )
} }
} }
} }
} }

@ -45,8 +45,15 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBar
import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
@ -188,7 +195,7 @@ fun MainView(
when (user) { when (user) {
null -> SettingsButton { navigation.onNavigateToSettings() } null -> SettingsButton { navigation.onNavigateToSettings() }
else -> { else -> {
Avatar(profile = user, size = 36) { navigation.onNavigateToSettings() } Avatar(profile = user, size = 36, { navigation.onNavigateToSettings() }, isFocusable=true)
} }
} }
} }

Loading…
Cancel
Save