Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +5 −7 Original line number Diff line number Diff line Loading @@ -736,14 +736,13 @@ private fun CategoryItemTwoPane( val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Surface( SelectableShortcutSurface( selected = selected, onClick = onClick, modifier = Modifier.semantics { role = Role.Tab } .heightIn(min = 64.dp) .fillMaxWidth() .focusable(interactionSource = interactionSource) .outlineFocusModifier( isFocused = isFocused, focusColor = MaterialTheme.colorScheme.secondary, Loading @@ -752,6 +751,7 @@ private fun CategoryItemTwoPane( ), shape = RoundedCornerShape(28.dp), color = colors.containerColor(selected).value, interactionSource = interactionSource ) { Row(Modifier.padding(horizontal = 24.dp), verticalAlignment = Alignment.CenterVertically) { ShortcutCategoryIcon( Loading Loading @@ -860,14 +860,12 @@ private fun ShortcutsSearchBar(onQueryChange: (String) -> Unit) { private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick: () -> Unit) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Surface( ClickableShortcutSurface( onClick = onClick, shape = RoundedCornerShape(24.dp), color = Color.Transparent, modifier = Modifier.semantics { role = Role.Button } .fillMaxWidth() .focusable(interactionSource = interactionSource) modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth(), interactionSource = interactionSource ) { Row( modifier = Loading packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.keyboard.shortcut.ui.composable import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.selection.selectable import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTonalElevationEnabled import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.modifiers.thenIf /** * A selectable surface with no default focus/hover indications. * * This composable is similar to [androidx.compose.material3.Surface], but removes default * focus/hover states to enable custom implementations. */ @Composable @NonRestartableComposable fun SelectableShortcutSurface( selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, interactionSource: MutableInteractionSource? = null, content: @Composable () -> Unit ) { @Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() } val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box( modifier = modifier .minimumInteractiveComponentSize() .surface( shape = shape, backgroundColor = surfaceColorAtElevation(color = color, elevation = absoluteElevation), border = border, shadowElevation = with(LocalDensity.current) { shadowElevation.toPx() } ) .selectable( selected = selected, interactionSource = interactionSource, indication = null, enabled = enabled, onClick = onClick ), propagateMinConstraints = true ) { content() } } } /** * A clickable surface with no default focus/hover indications. * * This composable is similar to [androidx.compose.material3.Surface], but removes default * focus/hover states to enable custom implementations. */ @Composable @NonRestartableComposable fun ClickableShortcutSurface( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, interactionSource: MutableInteractionSource? = null, content: @Composable () -> Unit ) { @Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() } val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box( modifier = modifier .minimumInteractiveComponentSize() .surface( shape = shape, backgroundColor = surfaceColorAtElevation(color = color, elevation = absoluteElevation), border = border, shadowElevation = with(LocalDensity.current) { shadowElevation.toPx() } ) .clickable( interactionSource = interactionSource, indication = null, enabled = enabled, onClick = onClick ), propagateMinConstraints = true ) { content() } } } @Composable private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color { return MaterialTheme.colorScheme.applyTonalElevation(color, elevation) } @Composable internal fun ColorScheme.applyTonalElevation(backgroundColor: Color, elevation: Dp): Color { val tonalElevationEnabled = LocalTonalElevationEnabled.current return if (backgroundColor == surface && tonalElevationEnabled) { surfaceColorAtElevation(elevation) } else { backgroundColor } } /** * Applies surface-related modifiers to a composable. * * This function adds background, border, and shadow effects to a composable. Also ensure the * composable is clipped to the given shape. * * @param shape The shape to apply to the composable's background, border, and clipping. * @param backgroundColor The background color to apply to the composable. * @param border An optional border to draw around the composable. * @param shadowElevation The size of the shadow below the surface. To prevent shadow creep, only * apply shadow elevation when absolutely necessary, such as when the surface requires visual * separation from a patterned background. Note that It will not affect z index of the Surface. If * you want to change the drawing order you can use `Modifier.zIndex`. * @return The modified Modifier instance with surface-related modifiers applied. */ @Stable private fun Modifier.surface( shape: Shape, backgroundColor: Color, border: BorderStroke?, shadowElevation: Float, ): Modifier { return this.thenIf(shadowElevation > 0f) { Modifier.graphicsLayer(shadowElevation = shadowElevation, shape = shape, clip = false) } .thenIf(border != null) { Modifier.border(border!!, shape) } .background(color = backgroundColor, shape = shape) .clip(shape) } Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +5 −7 Original line number Diff line number Diff line Loading @@ -736,14 +736,13 @@ private fun CategoryItemTwoPane( val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Surface( SelectableShortcutSurface( selected = selected, onClick = onClick, modifier = Modifier.semantics { role = Role.Tab } .heightIn(min = 64.dp) .fillMaxWidth() .focusable(interactionSource = interactionSource) .outlineFocusModifier( isFocused = isFocused, focusColor = MaterialTheme.colorScheme.secondary, Loading @@ -752,6 +751,7 @@ private fun CategoryItemTwoPane( ), shape = RoundedCornerShape(28.dp), color = colors.containerColor(selected).value, interactionSource = interactionSource ) { Row(Modifier.padding(horizontal = 24.dp), verticalAlignment = Alignment.CenterVertically) { ShortcutCategoryIcon( Loading Loading @@ -860,14 +860,12 @@ private fun ShortcutsSearchBar(onQueryChange: (String) -> Unit) { private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick: () -> Unit) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() Surface( ClickableShortcutSurface( onClick = onClick, shape = RoundedCornerShape(24.dp), color = Color.Transparent, modifier = Modifier.semantics { role = Role.Button } .fillMaxWidth() .focusable(interactionSource = interactionSource) modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth(), interactionSource = interactionSource ) { Row( modifier = Loading
packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt 0 → 100644 +199 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.keyboard.shortcut.ui.composable import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.selection.selectable import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTonalElevationEnabled import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.modifiers.thenIf /** * A selectable surface with no default focus/hover indications. * * This composable is similar to [androidx.compose.material3.Surface], but removes default * focus/hover states to enable custom implementations. */ @Composable @NonRestartableComposable fun SelectableShortcutSurface( selected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, interactionSource: MutableInteractionSource? = null, content: @Composable () -> Unit ) { @Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() } val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box( modifier = modifier .minimumInteractiveComponentSize() .surface( shape = shape, backgroundColor = surfaceColorAtElevation(color = color, elevation = absoluteElevation), border = border, shadowElevation = with(LocalDensity.current) { shadowElevation.toPx() } ) .selectable( selected = selected, interactionSource = interactionSource, indication = null, enabled = enabled, onClick = onClick ), propagateMinConstraints = true ) { content() } } } /** * A clickable surface with no default focus/hover indications. * * This composable is similar to [androidx.compose.material3.Surface], but removes default * focus/hover states to enable custom implementations. */ @Composable @NonRestartableComposable fun ClickableShortcutSurface( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, interactionSource: MutableInteractionSource? = null, content: @Composable () -> Unit ) { @Suppress("NAME_SHADOWING") val interactionSource = interactionSource ?: remember { MutableInteractionSource() } val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box( modifier = modifier .minimumInteractiveComponentSize() .surface( shape = shape, backgroundColor = surfaceColorAtElevation(color = color, elevation = absoluteElevation), border = border, shadowElevation = with(LocalDensity.current) { shadowElevation.toPx() } ) .clickable( interactionSource = interactionSource, indication = null, enabled = enabled, onClick = onClick ), propagateMinConstraints = true ) { content() } } } @Composable private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color { return MaterialTheme.colorScheme.applyTonalElevation(color, elevation) } @Composable internal fun ColorScheme.applyTonalElevation(backgroundColor: Color, elevation: Dp): Color { val tonalElevationEnabled = LocalTonalElevationEnabled.current return if (backgroundColor == surface && tonalElevationEnabled) { surfaceColorAtElevation(elevation) } else { backgroundColor } } /** * Applies surface-related modifiers to a composable. * * This function adds background, border, and shadow effects to a composable. Also ensure the * composable is clipped to the given shape. * * @param shape The shape to apply to the composable's background, border, and clipping. * @param backgroundColor The background color to apply to the composable. * @param border An optional border to draw around the composable. * @param shadowElevation The size of the shadow below the surface. To prevent shadow creep, only * apply shadow elevation when absolutely necessary, such as when the surface requires visual * separation from a patterned background. Note that It will not affect z index of the Surface. If * you want to change the drawing order you can use `Modifier.zIndex`. * @return The modified Modifier instance with surface-related modifiers applied. */ @Stable private fun Modifier.surface( shape: Shape, backgroundColor: Color, border: BorderStroke?, shadowElevation: Float, ): Modifier { return this.thenIf(shadowElevation > 0f) { Modifier.graphicsLayer(shadowElevation = shadowElevation, shape = shape, clip = false) } .thenIf(border != null) { Modifier.border(border!!, shape) } .background(color = backgroundColor, shape = shape) .clip(shape) }