diff options
| author | 2025-01-25 18:31:33 +0000 | |
|---|---|---|
| committer | 2025-01-27 17:00:33 +0000 | |
| commit | 5a0147f03387937ec30167bb01c03a4d43e776c5 (patch) | |
| tree | 9d2eae9461bd403c45436b402ac8183746247b4e | |
| parent | 10959b9e4b9a986428f827f0c494197c5a02ca56 (diff) | |
Redesigned Add Shortcut Dialog Edit Box
The add shortcut dialog has a box where the users' selected key
combination is shown. Previously this box was just a clickable surface
that displayed some text/icon composable.
There are some downsides to this design:
1. No blinking cursor to show the user that they're currently editing.
2. A11y - No talkback actions on double click despite talback reading
"double tap to activate"
The main consideration for not using a textfield composable previously
was that textfields can only show text, but keyboard keys can be
represented as text or glyphs(drawables) which traditional textfields
don't support.
This CL implements a custom InputField composable `OutlinedInputField`
which has all the benefits of a text field(blinking cursor, A11y
features) but rather than displaying just text, it generally supports
displaying any composable content which can be text, icon, or anything
else.this solution hence addresses both Downsides to the previous
design.
Test: Manual - Ensure the desired behaviour is observed in the add
shortcut dialog.
Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer
Fix: 390278358
Fix: 390102227
Fix: 390281127
Bug: 387995731
Change-Id: Ic5af2c9ba06a8cd0c204f742804d6202f2043e8a
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt | 160 |
1 files changed, 97 insertions, 63 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt index 6e9265167b7d..8f857a707e70 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt @@ -17,14 +17,10 @@ package com.android.systemui.keyboard.shortcut.ui.composable import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.collectIsFocusedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -38,13 +34,15 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ErrorOutline import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester @@ -55,6 +53,7 @@ import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.type +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.LiveRegionMode @@ -247,10 +246,11 @@ private fun ErrorMessageContainer(errorMessage: String) { lineHeight = 20.sp, fontWeight = FontWeight.W500, color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding(start = 24.dp).width(252.dp).semantics { - contentDescription = errorMessage - liveRegion = LiveRegionMode.Polite - }, + modifier = + Modifier.padding(start = 24.dp).width(252.dp).semantics { + contentDescription = errorMessage + liveRegion = LiveRegionMode.Polite + }, ) } } @@ -263,71 +263,76 @@ private fun SelectedKeyCombinationContainer( pressedKeys: List<ShortcutKey>, onConfirmSetShortcut: () -> Unit, ) { - val interactionSource = remember { MutableInteractionSource() } - val isFocused by interactionSource.collectIsFocusedAsState() - val outlineColor = - if (!isFocused) MaterialTheme.colorScheme.outline - else if (shouldShowError) MaterialTheme.colorScheme.error - else MaterialTheme.colorScheme.primary val focusRequester = remember { FocusRequester() } - + val focusManager = LocalFocusManager.current LaunchedEffect(Unit) { focusRequester.requestFocus() } - ClickableShortcutSurface( - onClick = {}, - color = Color.Transparent, - shape = RoundedCornerShape(50.dp), + OutlinedInputField( modifier = Modifier.padding(all = 16.dp) .sizeIn(minWidth = 332.dp, minHeight = 56.dp) - .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp)) + .focusRequester(focusRequester) + .focusProperties { canFocus = true } .onPreviewKeyEvent { keyEvent -> val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent) - if ( - !keyEventProcessed && - keyEvent.key == Key.Enter && - keyEvent.type == KeyEventType.KeyUp - ) { - onConfirmSetShortcut() + if (keyEventProcessed) { true - } else keyEventProcessed - } - .focusProperties { canFocus = true } // enables keyboard focus when in touch mode - .focusRequester(focusRequester), - interactionSource = interactionSource, - ) { - Row( - modifier = Modifier.padding(start = 24.dp, top = 16.dp, end = 16.dp, bottom = 16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - if (pressedKeys.isEmpty()) { - PressKeyPrompt() + } else { + if (keyEvent.type == KeyEventType.KeyUp) { + when (keyEvent.key) { + Key.Enter -> { + onConfirmSetShortcut() + return@onPreviewKeyEvent true + } + Key.DirectionDown -> { + focusManager.moveFocus(FocusDirection.Down) + return@onPreviewKeyEvent true + } + else -> return@onPreviewKeyEvent false + } + } else false + } + }, + trailingIcon = { ErrorIcon(shouldShowError) }, + isError = shouldShowError, + placeholder = { PressKeyPrompt() }, + content = + if (pressedKeys.isNotEmpty()) { + { PressedKeysTextContainer(pressedKeys) } } else { - PressedKeysTextContainer(pressedKeys) - } - Spacer(modifier = Modifier.weight(1f)) - if (shouldShowError) { - Icon( - imageVector = Icons.Default.ErrorOutline, - contentDescription = null, - modifier = Modifier.size(20.dp), - tint = MaterialTheme.colorScheme.error, - ) - } - } + null + }, + ) +} + +@Composable +private fun ErrorIcon(shouldShowError: Boolean) { + if (shouldShowError) { + Icon( + imageVector = Icons.Default.ErrorOutline, + contentDescription = null, + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colorScheme.error, + ) } } @Composable -private fun RowScope.PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) { - pressedKeys.forEachIndexed { keyIndex, key -> - if (keyIndex > 0) { - ShortcutKeySeparator() - } - if (key is ShortcutKey.Text) { - ShortcutTextKey(key) - } else if (key is ShortcutKey.Icon) { - ShortcutIconKey(key) +private fun PressedKeysTextContainer(pressedKeys: List<ShortcutKey>) { + Row( + modifier = + Modifier.semantics(mergeDescendants = true) { liveRegion = LiveRegionMode.Polite }, + verticalAlignment = Alignment.CenterVertically, + ) { + pressedKeys.forEachIndexed { keyIndex, key -> + if (keyIndex > 0) { + ShortcutKeySeparator() + } + if (key is ShortcutKey.Text) { + ShortcutTextKey(key) + } else if (key is ShortcutKey.Icon) { + ShortcutIconKey(key) + } } } } @@ -344,7 +349,7 @@ private fun ShortcutKeySeparator() { } @Composable -private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) { +private fun ShortcutIconKey(key: ShortcutKey.Icon) { Icon( painter = when (key) { @@ -352,7 +357,8 @@ private fun RowScope.ShortcutIconKey(key: ShortcutKey.Icon) { is ShortcutKey.Icon.DrawableIcon -> rememberDrawablePainter(drawable = key.drawable) }, contentDescription = null, - modifier = Modifier.align(Alignment.CenterVertically).height(24.dp), + modifier = + Modifier.height(24.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } @@ -403,7 +409,7 @@ private fun Description(text: String) { .width(316.dp) .wrapContentSize(Alignment.Center), color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Center + textAlign = TextAlign.Center, ) } @@ -467,3 +473,31 @@ private fun PlusIconContainer() { modifier = Modifier.padding(vertical = 12.dp).size(24.dp).wrapContentSize(Alignment.Center), ) } + +@Composable +private fun OutlinedInputField( + content: @Composable (() -> Unit)?, + placeholder: @Composable () -> Unit, + trailingIcon: @Composable () -> Unit, + isError: Boolean, + modifier: Modifier = Modifier, +) { + OutlinedTextField( + value = "", + onValueChange = {}, + placeholder = if (content == null) placeholder else null, + prefix = content, + singleLine = true, + modifier = modifier, + trailingIcon = trailingIcon, + colors = + OutlinedTextFieldDefaults.colors() + .copy( + focusedIndicatorColor = MaterialTheme.colorScheme.primary, + unfocusedIndicatorColor = MaterialTheme.colorScheme.outline, + errorIndicatorColor = MaterialTheme.colorScheme.error, + ), + shape = RoundedCornerShape(50.dp), + isError = isError, + ) +} |