From 566a6cd12055ae7a2827f96dc12ec6694f2fb8b4 Mon Sep 17 00:00:00 2001 From: darken Date: Tue, 14 Apr 2026 11:36:57 +0200 Subject: [PATCH] feat(settings): Replace rename-failure snackbar with dialog --- .../ui/devicesettings/DeviceSettingsScreen.kt | 56 ++++++++++++++++--- app/src/main/res/values/strings.xml | 2 + 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt index 35557ed6..e5964ec8 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/devicesettings/DeviceSettingsScreen.kt @@ -6,6 +6,8 @@ import androidx.compose.foundation.Canvas import androidx.compose.foundation.clickable import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -66,13 +68,17 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -115,6 +121,7 @@ fun DeviceSettingsScreenHost( val context = LocalContext.current val snackbarHostState = remember { SnackbarHostState() } + var showRenameUnavailableDialog by rememberSaveable { mutableStateOf(false) } LaunchedEffect(Unit) { vm.events.collect { event -> when (event) { @@ -127,15 +134,22 @@ fun DeviceSettingsScreenHost( ) } DeviceSettingsViewModel.Event.SystemRenameUnavailable -> { - snackbarHostState.showSnackbar( - context.getString(R.string.device_settings_rename_system_unavailable), - duration = androidx.compose.material3.SnackbarDuration.Long, - ) + showRenameUnavailableDialog = true } } } } + if (showRenameUnavailableDialog) { + SystemRenameUnavailableDialog( + onOpenBluetoothSettings = { + showRenameUnavailableDialog = false + context.startActivity(Intent(Settings.ACTION_BLUETOOTH_SETTINGS)) + }, + onDismiss = { showRenameUnavailableDialog = false }, + ) + } + val state by vm.state.collectAsStateWithLifecycle(initialValue = null) val currentState = state ?: return @@ -1222,6 +1236,7 @@ private fun RenameDialog( onDismiss: () -> Unit, ) { var textValue by remember { mutableStateOf(currentName) } + val focusRequester = remember { FocusRequester() } // The decoder in DefaultAapDeviceProfile only round-trips printable ASCII (0x20..0x7E), // so even if the device accepts a UTF-8 name we can't display it back correctly. Accept any @@ -1229,6 +1244,9 @@ private fun RenameDialog( // understands why Rename is disabled. val hasInvalidAscii = textValue.any { it.code !in 0x20..0x7E } val isValid = textValue.isNotBlank() && !hasInvalidAscii + val canConfirm = isValid && textValue != currentName + + LaunchedEffect(Unit) { focusRequester.requestFocus() } androidx.compose.material3.AlertDialog( onDismissRequest = onDismiss, @@ -1248,13 +1266,15 @@ private fun RenameDialog( supportingText = if (hasInvalidAscii) { { Text(stringResource(R.string.device_settings_rename_invalid_ascii)) } } else null, - modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { if (canConfirm) onConfirm(textValue) }), + modifier = Modifier.fillMaxWidth().focusRequester(focusRequester), ) }, confirmButton = { androidx.compose.material3.TextButton( - onClick = { if (isValid) onConfirm(textValue) }, - enabled = isValid && textValue != currentName, + onClick = { if (canConfirm) onConfirm(textValue) }, + enabled = canConfirm, ) { Text(stringResource(R.string.device_settings_rename_confirm)) } @@ -1267,6 +1287,28 @@ private fun RenameDialog( ) } +@Composable +private fun SystemRenameUnavailableDialog( + onOpenBluetoothSettings: () -> Unit, + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(stringResource(R.string.device_settings_rename_system_unavailable_title)) }, + text = { Text(stringResource(R.string.device_settings_rename_system_unavailable)) }, + confirmButton = { + TextButton(onClick = onOpenBluetoothSettings) { + Text(stringResource(R.string.device_settings_rename_system_unavailable_bt_settings_action)) + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text(stringResource(android.R.string.ok)) + } + }, + ) +} + @Composable private fun NoiseControlCombined( currentMode: AapSetting.AncMode.Value, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e4101b29..e5a9f72f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -500,7 +500,9 @@ Device name Rename Only ASCII characters are supported + Rename incomplete Android didn\'t let us rename the device here. To update the name in Bluetooth settings, rename it there or re-pair the device. + Bluetooth Settings Could not apply setting: %1$s Connected Devices Other devices currently connected to these AirPods