diff options
| author | 2024-10-30 13:20:21 +0000 | |
|---|---|---|
| committer | 2024-10-30 13:20:21 +0000 | |
| commit | 62ced8cd1452d32f54556d4007fe1bad1ca634c4 (patch) | |
| tree | b909dbc83708c465b2622191939601099a9a565d | |
| parent | 171be9f0129230d705fadaf6a40358149a08cd83 (diff) | |
| parent | e2b71c9e21b258827d293302d5eeedd4a4d50680 (diff) | |
Merge "Added buttons for entering and exiting customize mode" into main
6 files changed, 241 insertions, 13 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index d124c02b1808..d50a92b4e726 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1667,3 +1667,12 @@ flag { description: "Expands the shade on long press of any status bar" bug: "371224114" } + + +flag { + name: "keyboard_shortcut_helper_shortcut_customizer" + namespace: "systemui" + description: "An implementation of shortcut customizations through shortcut helper." + bug: "365064144" +} + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 5e4cb7579737..96021631415f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3733,6 +3733,10 @@ <!-- Title at the top of the keyboard shortcut helper UI. The helper is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_title">Keyboard shortcuts</string> + <!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper + is a component that shows the user which keyboard shortcuts they can use. + [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string> <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user hasn't typed in anything in the search box yet. The helper is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> @@ -3744,6 +3748,14 @@ use. The helper shows shortcuts in categories, which can be collapsed or expanded. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string> + <!-- Description text of the button that allows user to customize shortcuts in keyboard + shortcut helper The helper is a component that shows the user which keyboard shortcuts + they can use. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_customize_button_text">Customize</string> + <!-- Description text of the button that allows user to exit shortcut customization mode in + keyboard shortcut helper The helper is a component that shows the user which keyboard + shortcuts they can use. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_done_button_text">Done</string> <!-- Content description of the icon that allows to expand a keyboard shortcut helper category panel. The helper is a component that shows the user which keyboard shortcuts they can use. The helper shows shortcuts in categories, which can be collapsed or expanded. diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt index 5cade686ae09..d53705605901 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt @@ -52,8 +52,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.OpenInNew +import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -69,6 +71,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -169,6 +172,7 @@ private fun ActiveShortcutHelper( selectedCategoryType, onCategorySelected = { selectedCategoryType = it }, onKeyboardSettingsClicked, + shortcutsUiState.isShortcutCustomizerFlagEnabled, ) } } @@ -357,10 +361,29 @@ private fun ShortcutHelperTwoPane( selectedCategoryType: ShortcutCategoryType?, onCategorySelected: (ShortcutCategoryType?) -> Unit, onKeyboardSettingsClicked: () -> Unit, + isShortcutCustomizerFlagEnabled: Boolean, ) { val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType } + var isCustomizeModeEntered by remember { mutableStateOf(false) } + val isCustomizing by + remember(isCustomizeModeEntered, isShortcutCustomizerFlagEnabled) { + derivedStateOf { isCustomizeModeEntered && isCustomizeModeEntered } + } + Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) { - TitleBar() + Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Box(modifier = Modifier.padding(start = 202.dp).width(412.dp)) { + TitleBar(isCustomizing) + } + Spacer(modifier = Modifier.weight(1f)) + if (isShortcutCustomizerFlagEnabled) { + if (isCustomizeModeEntered) { + DoneButton(onClick = { isCustomizeModeEntered = false }) + } else { + CustomizeButton(onClick = { isCustomizeModeEntered = true }) + } + } + } Spacer(modifier = Modifier.height(12.dp)) Row(Modifier.fillMaxWidth()) { StartSidePanel( @@ -372,13 +395,46 @@ private fun ShortcutHelperTwoPane( onCategoryClicked = { onCategorySelected(it.type) }, ) Spacer(modifier = Modifier.width(24.dp)) - EndSidePanel(searchQuery, Modifier.fillMaxSize().padding(top = 8.dp), selectedCategory) + EndSidePanel( + searchQuery, + Modifier.fillMaxSize().padding(top = 8.dp), + selectedCategory, + isCustomizing = isCustomizing, + ) } } } @Composable -private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategoryUi?) { +private fun CustomizeButton(onClick: () -> Unit) { + ShortcutHelperButton( + onClick = onClick, + color = MaterialTheme.colorScheme.secondaryContainer, + width = 133.dp, + iconSource = IconSource(imageVector = Icons.Default.Tune), + text = stringResource(id = R.string.shortcut_helper_customize_button_text), + contentColor = MaterialTheme.colorScheme.onSecondaryContainer, + ) +} + +@Composable +private fun DoneButton(onClick: () -> Unit) { + ShortcutHelperButton( + onClick = onClick, + color = MaterialTheme.colorScheme.primary, + width = 69.dp, + text = stringResource(R.string.shortcut_helper_done_button_text), + contentColor = MaterialTheme.colorScheme.onPrimary, + ) +} + +@Composable +private fun EndSidePanel( + searchQuery: String, + modifier: Modifier, + category: ShortcutCategoryUi?, + isCustomizing: Boolean, +) { val listState = rememberLazyListState() LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) } if (category == null) { @@ -387,7 +443,11 @@ private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: Shor } LazyColumn(modifier = modifier, state = listState) { items(category.subCategories) { subcategory -> - SubCategoryContainerDualPane(searchQuery = searchQuery, subCategory = subcategory) + SubCategoryContainerDualPane( + searchQuery = searchQuery, + subCategory = subcategory, + isCustomizing = isCustomizing, + ) Spacer(modifier = Modifier.height(8.dp)) } } @@ -412,7 +472,11 @@ private fun NoSearchResultsText(horizontalPadding: Dp, fillHeight: Boolean) { } @Composable -private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: ShortcutSubCategory) { +private fun SubCategoryContainerDualPane( + searchQuery: String, + subCategory: ShortcutSubCategory, + isCustomizing: Boolean, +) { Surface( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(28.dp), @@ -432,6 +496,7 @@ private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: Short modifier = Modifier.padding(vertical = 8.dp), searchQuery = searchQuery, shortcut = shortcut, + isCustomizing = isCustomizing, ) } } @@ -448,7 +513,12 @@ private fun SubCategoryTitle(title: String) { } @Composable -private fun Shortcut(modifier: Modifier, searchQuery: String, shortcut: ShortcutModel) { +private fun Shortcut( + modifier: Modifier, + searchQuery: String, + shortcut: ShortcutModel, + isCustomizing: Boolean = false, +) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() val focusColor = MaterialTheme.colorScheme.secondary @@ -471,7 +541,7 @@ private fun Shortcut(modifier: Modifier, searchQuery: String, shortcut: Shortcut ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut) } Spacer(modifier = Modifier.width(24.dp)) - ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut) + ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut, isCustomizing) } } @@ -495,7 +565,11 @@ fun ShortcutIcon( @OptIn(ExperimentalLayoutApi::class) @Composable -private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: ShortcutModel) { +private fun ShortcutKeyCombinations( + modifier: Modifier = Modifier, + shortcut: ShortcutModel, + isCustomizing: Boolean = false, +) { FlowRow( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -507,6 +581,25 @@ private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: Sho } ShortcutCommand(command) } + if (isCustomizing) { + Spacer(modifier = Modifier.width(16.dp)) + ShortcutHelperButton( + modifier = + Modifier.border( + width = 1.dp, + color = MaterialTheme.colorScheme.outline, + shape = CircleShape, + ), + onClick = {}, + color = Color.Transparent, + width = 32.dp, + height = 32.dp, + iconSource = IconSource(imageVector = Icons.Default.Add), + contentColor = MaterialTheme.colorScheme.primary, + contentPaddingVertical = 0.dp, + contentPaddingHorizontal = 0.dp, + ) + } } } @@ -700,12 +793,18 @@ private fun CategoryItemTwoPane( @Composable @OptIn(ExperimentalMaterial3Api::class) -private fun TitleBar() { +private fun TitleBar(isCustomizing: Boolean = false) { + val text = + if (isCustomizing) { + stringResource(R.string.shortcut_helper_customize_mode_title) + } else { + stringResource(R.string.shortcut_helper_title) + } CenterAlignedTopAppBar( colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent), title = { Text( - text = stringResource(R.string.shortcut_helper_title), + text = text, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineSmall, ) @@ -753,14 +852,12 @@ private fun ShortcutsSearchBar(onQueryChange: (String) -> Unit) { @Composable private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick: () -> Unit) { - val interactionSource = remember { MutableInteractionSource() } ClickableShortcutSurface( onClick = onClick, shape = RoundedCornerShape(24.dp), color = Color.Transparent, modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth().padding(horizontal = 12.dp), - interactionSource = interactionSource, interactionsConfig = InteractionsConfig( hoverOverlayColor = MaterialTheme.colorScheme.onSurface, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt index f64d59a26893..435968ee79ca 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt @@ -27,13 +27,24 @@ import androidx.compose.foundation.interaction.InteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ColorScheme +import androidx.compose.material3.Icon import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTonalElevationEnabled import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.surfaceColorAtElevation @@ -43,6 +54,7 @@ import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset @@ -57,11 +69,16 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.node.DelegatableNode import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex -import com.android.compose.modifiers.thenIf import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.compose.modifiers.thenIf +import com.android.systemui.keyboard.shortcut.ui.model.IconSource /** * A selectable surface with no default focus/hover indications. @@ -175,6 +192,96 @@ fun ClickableShortcutSurface( } } +/** + * A composable that provides a button with a customizable icon and text, designed to be re-used + * across shortcut helper/customizer. Supports defaults hover/focus/pressed states used across + * shortcut helper. + * + * This button utilizes [ClickableShortcutSurface] to provide a clickable surface with hover and + * pressed states, and a focus outline. + * + * The content of the button can be an icon (from [IconSource]) and/or text. + * + * @param modifier The modifier to be applied to the button. + * @param onClick The callback function that will be invoked when the button is clicked. + * @param shape The shape of the button. Defaults to a rounded corner shape used across shortcut + * helper. + * @param color The background color of the button. + * @param width The width of the button. + * @param height The height of the button. Defaults to 40.dp as often used in shortcut helper + * @param iconSource The source of the icon to be displayed. Defaults to an empty [IconSource]. + * @param text The text to be displayed. Defaults to null. + * @param contentColor The color of the icon and text. + * @param contentPaddingHorizontal The horizontal padding of the content. Defaults to 16.dp. + * @param contentPaddingVertical The vertical padding of the content. Defaults to 10.dp. + */ +@Composable +fun ShortcutHelperButton( + modifier: Modifier = Modifier, + onClick: () -> Unit, + shape: Shape = RoundedCornerShape(360.dp), + color: Color, + width: Dp, + height: Dp = 40.dp, + iconSource: IconSource = IconSource(), + text: String? = null, + contentColor: Color, + contentPaddingHorizontal: Dp = 16.dp, + contentPaddingVertical: Dp = 10.dp, +) { + ClickableShortcutSurface( + onClick = onClick, + shape = shape, + color = color, + modifier = modifier.semantics { role = Role.Button }.width(width).height(height), + interactionsConfig = + InteractionsConfig( + hoverOverlayColor = MaterialTheme.colorScheme.onSurface, + hoverOverlayAlpha = 0.11f, + pressedOverlayColor = MaterialTheme.colorScheme.onSurface, + pressedOverlayAlpha = 0.15f, + focusOutlineColor = MaterialTheme.colorScheme.secondary, + focusOutlineStrokeWidth = 3.dp, + focusOutlinePadding = 2.dp, + surfaceCornerRadius = 28.dp, + focusOutlineCornerRadius = 33.dp, + ), + ) { + Row( + modifier = + Modifier.padding( + horizontal = contentPaddingHorizontal, + vertical = contentPaddingVertical, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + ) { + if (iconSource.imageVector != null) { + Icon( + tint = contentColor, + imageVector = iconSource.imageVector, + contentDescription = null, + modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center), + ) + } + + if (iconSource.imageVector != null && text != null) { + Spacer(modifier = Modifier.weight(1f)) + } + + if (text != null) { + Text( + text, + color = contentColor, + fontSize = 14.sp, + style = MaterialTheme.typography.labelLarge, + modifier = Modifier.wrapContentSize(Alignment.Center), + ) + } + } + } +} + @Composable private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color { return MaterialTheme.colorScheme.applyTonalElevation(color, elevation) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt index 8f23261c3ef4..02b0b43ea979 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt @@ -24,6 +24,7 @@ sealed interface ShortcutsUiState { val searchQuery: String, val shortcutCategories: List<ShortcutCategoryUi>, val defaultSelectedCategory: ShortcutCategoryType?, + val isShortcutCustomizerFlagEnabled: Boolean = false, ) : ShortcutsUiState data object Inactive : ShortcutsUiState diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt index 20d09ede39e3..912bfe99cea8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt @@ -26,6 +26,7 @@ import androidx.compose.material.icons.filled.Keyboard import androidx.compose.material.icons.filled.Tv import androidx.compose.material.icons.filled.VerticalSplit import com.android.compose.ui.graphics.painter.DrawablePainter +import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor @@ -86,6 +87,7 @@ constructor( searchQuery = query, shortcutCategories = shortcutCategoriesUi, defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories), + isShortcutCustomizerFlagEnabled = keyboardShortcutHelperShortcutCustomizer(), ) } } |