Loading packages/SystemUI/aconfig/systemui.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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" } packages/SystemUI/res/values/strings.xml +12 −0 Original line number Diff line number Diff line Loading @@ -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] --> Loading @@ -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. Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +109 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -169,6 +172,7 @@ private fun ActiveShortcutHelper( selectedCategoryType, onCategorySelected = { selectedCategoryType = it }, onKeyboardSettingsClicked, shortcutsUiState.isShortcutCustomizerFlagEnabled, ) } } Loading Loading @@ -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( Loading @@ -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) { Loading @@ -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)) } } Loading @@ -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), Loading @@ -432,6 +496,7 @@ private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: Short modifier = Modifier.padding(vertical = 8.dp), searchQuery = searchQuery, shortcut = shortcut, isCustomizing = isCustomizing, ) } } Loading @@ -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 Loading @@ -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) } } Loading @@ -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), Loading @@ -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, ) } } } Loading Loading @@ -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, ) Loading Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt +108 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading Loading @@ -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) Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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" }
packages/SystemUI/res/values/strings.xml +12 −0 Original line number Diff line number Diff line Loading @@ -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] --> Loading @@ -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. Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +109 −12 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -169,6 +172,7 @@ private fun ActiveShortcutHelper( selectedCategoryType, onCategorySelected = { selectedCategoryType = it }, onKeyboardSettingsClicked, shortcutsUiState.isShortcutCustomizerFlagEnabled, ) } } Loading Loading @@ -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( Loading @@ -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) { Loading @@ -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)) } } Loading @@ -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), Loading @@ -432,6 +496,7 @@ private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: Short modifier = Modifier.padding(vertical = 8.dp), searchQuery = searchQuery, shortcut = shortcut, isCustomizing = isCustomizing, ) } } Loading @@ -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 Loading @@ -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) } } Loading @@ -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), Loading @@ -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, ) } } } Loading Loading @@ -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, ) Loading Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt +108 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading Loading @@ -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) Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading