diff options
3 files changed, 620 insertions, 66 deletions
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt new file mode 100644 index 000000000000..7db1ca1384f9 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -0,0 +1,604 @@ +/* + * Copyright 2022 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.settingslib.spa.widget.scaffold + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.AnimationState +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.core.FastOutLinearInEasing +import androidx.compose.animation.core.animateDecay +import androidx.compose.animation.core.animateTo +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.gestures.rememberDraggableState +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.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProvideTextStyle +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.TopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.layout.AlignmentLine +import androidx.compose.ui.layout.LastBaseline +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import com.android.settingslib.spa.framework.compose.horizontalValues +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsTheme +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.roundToInt + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun CustomizedTopAppBar( + title: @Composable () -> Unit, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, +) { + SingleRowTopAppBar( + title = title, + titleTextStyle = MaterialTheme.typography.titleMedium, + navigationIcon = navigationIcon, + actions = actions, + windowInsets = TopAppBarDefaults.windowInsets, + colors = topAppBarColors(), + ) +} + +/** + * The customized LargeTopAppBar for Settings. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun CustomizedLargeTopAppBar( + title: String, + modifier: Modifier = Modifier, + navigationIcon: @Composable () -> Unit = {}, + actions: @Composable RowScope.() -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null, +) { + TwoRowsTopAppBar( + title = { Title(title = title, maxLines = 2) }, + titleTextStyle = MaterialTheme.typography.displaySmall, + smallTitleTextStyle = MaterialTheme.typography.titleMedium, + titleBottomPadding = LargeTitleBottomPadding, + smallTitle = { Title(title = title, maxLines = 1) }, + modifier = modifier, + navigationIcon = navigationIcon, + actions = actions, + colors = topAppBarColors(), + windowInsets = TopAppBarDefaults.windowInsets, + maxHeight = 176.dp, + pinnedHeight = ContainerHeight, + scrollBehavior = scrollBehavior, + ) +} + +@Composable +private fun Title(title: String, maxLines: Int = Int.MAX_VALUE) { + Text( + text = title, + modifier = Modifier + .padding( + WindowInsets.navigationBars + .asPaddingValues() + .horizontalValues() + ) + .padding(horizontal = SettingsDimension.itemPaddingAround), + overflow = TextOverflow.Ellipsis, + maxLines = maxLines, + ) +} + +@Composable +private fun topAppBarColors() = TopAppBarColors( + containerColor = MaterialTheme.colorScheme.background, + scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader, + navigationIconContentColor = MaterialTheme.colorScheme.onSurface, + titleContentColor = MaterialTheme.colorScheme.onSurface, + actionIconContentColor = MaterialTheme.colorScheme.onSurfaceVariant, +) + +/** + * Represents the colors used by a top app bar in different states. + * This implementation animates the container color according to the top app bar scroll state. It + * does not animate the leading, headline, or trailing colors. + */ +@Stable +private class TopAppBarColors( + val containerColor: Color, + val scrolledContainerColor: Color, + val navigationIconContentColor: Color, + val titleContentColor: Color, + val actionIconContentColor: Color, +) { + + /** + * Represents the container color used for the top app bar. + * + * A [colorTransitionFraction] provides a percentage value that can be used to generate a color. + * Usually, an app bar implementation will pass in a [colorTransitionFraction] read from + * the [TopAppBarState.collapsedFraction] or the [TopAppBarState.overlappedFraction]. + * + * @param colorTransitionFraction a `0.0` to `1.0` value that represents a color transition + * percentage + */ + @Composable + fun containerColor(colorTransitionFraction: Float): Color { + return lerp( + containerColor, + scrolledContainerColor, + FastOutLinearInEasing.transform(colorTransitionFraction) + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other !is TopAppBarColors) return false + + if (containerColor != other.containerColor) return false + if (scrolledContainerColor != other.scrolledContainerColor) return false + if (navigationIconContentColor != other.navigationIconContentColor) return false + if (titleContentColor != other.titleContentColor) return false + if (actionIconContentColor != other.actionIconContentColor) return false + + return true + } + + override fun hashCode(): Int { + var result = containerColor.hashCode() + result = 31 * result + scrolledContainerColor.hashCode() + result = 31 * result + navigationIconContentColor.hashCode() + result = 31 * result + titleContentColor.hashCode() + result = 31 * result + actionIconContentColor.hashCode() + + return result + } +} + +/** + * A single-row top app bar that is designed to be called by the small and center aligned top app + * bar composables. + */ +@Composable +private fun SingleRowTopAppBar( + title: @Composable () -> Unit, + titleTextStyle: TextStyle, + navigationIcon: @Composable () -> Unit, + actions: @Composable (RowScope.() -> Unit), + windowInsets: WindowInsets, + colors: TopAppBarColors, +) { + // Wrap the given actions in a Row. + val actionsRow = @Composable { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + content = actions + ) + } + + // Compose a Surface with a TopAppBarLayout content. + Surface(color = colors.scrolledContainerColor) { + val height = LocalDensity.current.run { ContainerHeight.toPx() } + TopAppBarLayout( + modifier = Modifier + .windowInsetsPadding(windowInsets) + // clip after padding so we don't show the title over the inset area + .clipToBounds(), + heightPx = height, + navigationIconContentColor = colors.navigationIconContentColor, + titleContentColor = colors.titleContentColor, + actionIconContentColor = colors.actionIconContentColor, + title = title, + titleTextStyle = titleTextStyle, + titleAlpha = 1f, + titleVerticalArrangement = Arrangement.Center, + titleHorizontalArrangement = Arrangement.Start, + titleBottomPadding = 0, + hideTitleSemantics = false, + navigationIcon = navigationIcon, + actions = actionsRow, + ) + } +} + +/** + * A two-rows top app bar that is designed to be called by the Large and Medium top app bar + * composables. + * + * @throws [IllegalArgumentException] if the given [maxHeight] is equal or smaller than the + * [pinnedHeight] + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun TwoRowsTopAppBar( + modifier: Modifier = Modifier, + title: @Composable () -> Unit, + titleTextStyle: TextStyle, + titleBottomPadding: Dp, + smallTitle: @Composable () -> Unit, + smallTitleTextStyle: TextStyle, + navigationIcon: @Composable () -> Unit, + actions: @Composable RowScope.() -> Unit, + windowInsets: WindowInsets, + colors: TopAppBarColors, + maxHeight: Dp, + pinnedHeight: Dp, + scrollBehavior: TopAppBarScrollBehavior? +) { + if (maxHeight <= pinnedHeight) { + throw IllegalArgumentException( + "A TwoRowsTopAppBar max height should be greater than its pinned height" + ) + } + val pinnedHeightPx: Float + val maxHeightPx: Float + val titleBottomPaddingPx: Int + LocalDensity.current.run { + pinnedHeightPx = pinnedHeight.toPx() + maxHeightPx = maxHeight.toPx() + titleBottomPaddingPx = titleBottomPadding.roundToPx() + } + + // Sets the app bar's height offset limit to hide just the bottom title area and keep top title + // visible when collapsed. + SideEffect { + if (scrollBehavior?.state?.heightOffsetLimit != pinnedHeightPx - maxHeightPx) { + scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx + } + } + + // Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the + // bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or + // collapse. + // This will potentially animate or interpolate a transition between the container color and the + // container's scrolled color according to the app bar's scroll state. + val colorTransitionFraction = scrollBehavior?.state?.collapsedFraction ?: 0f + val appBarContainerColor by rememberUpdatedState(colors.containerColor(colorTransitionFraction)) + + // Wrap the given actions in a Row. + val actionsRow = @Composable { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + content = actions + ) + } + val topTitleAlpha = TopTitleAlphaEasing.transform(colorTransitionFraction) + val bottomTitleAlpha = 1f - colorTransitionFraction + // Hide the top row title semantics when its alpha value goes below 0.5 threshold. + // Hide the bottom row title semantics when the top title semantics are active. + val hideTopRowSemantics = colorTransitionFraction < 0.5f + val hideBottomRowSemantics = !hideTopRowSemantics + + // Set up support for resizing the top app bar when vertically dragging the bar itself. + val appBarDragModifier = if (scrollBehavior != null && !scrollBehavior.isPinned) { + Modifier.draggable( + orientation = Orientation.Vertical, + state = rememberDraggableState { delta -> + scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta + }, + onDragStopped = { velocity -> + settleAppBar( + scrollBehavior.state, + velocity, + scrollBehavior.flingAnimationSpec, + scrollBehavior.snapAnimationSpec + ) + } + ) + } else { + Modifier + } + + Surface(modifier = modifier.then(appBarDragModifier), color = appBarContainerColor) { + Column { + TopAppBarLayout( + modifier = Modifier + .windowInsetsPadding(windowInsets) + // clip after padding so we don't show the title over the inset area + .clipToBounds(), + heightPx = pinnedHeightPx, + navigationIconContentColor = colors.navigationIconContentColor, + titleContentColor = colors.titleContentColor, + actionIconContentColor = colors.actionIconContentColor, + title = smallTitle, + titleTextStyle = smallTitleTextStyle, + titleAlpha = topTitleAlpha, + titleVerticalArrangement = Arrangement.Center, + titleHorizontalArrangement = Arrangement.Start, + titleBottomPadding = 0, + hideTitleSemantics = hideTopRowSemantics, + navigationIcon = navigationIcon, + actions = actionsRow, + ) + TopAppBarLayout( + modifier = Modifier.clipToBounds(), + heightPx = maxHeightPx - pinnedHeightPx + (scrollBehavior?.state?.heightOffset + ?: 0f), + navigationIconContentColor = colors.navigationIconContentColor, + titleContentColor = colors.titleContentColor, + actionIconContentColor = colors.actionIconContentColor, + title = title, + titleTextStyle = titleTextStyle, + titleAlpha = bottomTitleAlpha, + titleVerticalArrangement = Arrangement.Bottom, + titleHorizontalArrangement = Arrangement.Start, + titleBottomPadding = titleBottomPaddingPx, + hideTitleSemantics = hideBottomRowSemantics, + navigationIcon = {}, + actions = {} + ) + } + } +} + +/** + * The base [Layout] for all top app bars. This function lays out a top app bar navigation icon + * (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and + * the actions are optional. + * + * @param heightPx the total height this layout is capped to + * @param navigationIconContentColor the content color that will be applied via a + * [LocalContentColor] when composing the navigation icon + * @param titleContentColor the color that will be applied via a [LocalContentColor] when composing + * the title + * @param actionIconContentColor the content color that will be applied via a [LocalContentColor] + * when composing the action icons + * @param title the top app bar title (header) + * @param titleTextStyle the title's text style + * @param modifier a [Modifier] + * @param titleAlpha the title's alpha + * @param titleVerticalArrangement the title's vertical arrangement + * @param titleHorizontalArrangement the title's horizontal arrangement + * @param titleBottomPadding the title's bottom padding + * @param hideTitleSemantics hides the title node from the semantic tree. Apply this + * boolean when this layout is part of a [TwoRowsTopAppBar] to hide the title's semantics + * from accessibility services. This is needed to avoid having multiple titles visible to + * accessibility services at the same time, when animating between collapsed / expanded states. + * @param navigationIcon a navigation icon [Composable] + * @param actions actions [Composable] + */ +@Composable +private fun TopAppBarLayout( + modifier: Modifier, + heightPx: Float, + navigationIconContentColor: Color, + titleContentColor: Color, + actionIconContentColor: Color, + title: @Composable () -> Unit, + titleTextStyle: TextStyle, + titleAlpha: Float, + titleVerticalArrangement: Arrangement.Vertical, + titleHorizontalArrangement: Arrangement.Horizontal, + titleBottomPadding: Int, + hideTitleSemantics: Boolean, + navigationIcon: @Composable () -> Unit, + actions: @Composable () -> Unit, +) { + Layout( + { + Box( + Modifier + .layoutId("navigationIcon") + .padding(start = TopAppBarHorizontalPadding) + ) { + CompositionLocalProvider( + LocalContentColor provides navigationIconContentColor, + content = navigationIcon + ) + } + Box( + Modifier + .layoutId("title") + .padding(horizontal = TopAppBarHorizontalPadding) + .then(if (hideTitleSemantics) Modifier.clearAndSetSemantics { } else Modifier) + .graphicsLayer(alpha = titleAlpha) + ) { + ProvideTextStyle(value = titleTextStyle) { + CompositionLocalProvider( + LocalContentColor provides titleContentColor, + content = title + ) + } + } + Box( + Modifier + .layoutId("actionIcons") + .padding(end = TopAppBarHorizontalPadding) + ) { + CompositionLocalProvider( + LocalContentColor provides actionIconContentColor, + content = actions + ) + } + }, + modifier = modifier + ) { measurables, constraints -> + val navigationIconPlaceable = + measurables.first { it.layoutId == "navigationIcon" } + .measure(constraints.copy(minWidth = 0)) + val actionIconsPlaceable = + measurables.first { it.layoutId == "actionIcons" } + .measure(constraints.copy(minWidth = 0)) + + val maxTitleWidth = if (constraints.maxWidth == Constraints.Infinity) { + constraints.maxWidth + } else { + (constraints.maxWidth - navigationIconPlaceable.width - actionIconsPlaceable.width) + .coerceAtLeast(0) + } + val titlePlaceable = + measurables.first { it.layoutId == "title" } + .measure(constraints.copy(minWidth = 0, maxWidth = maxTitleWidth)) + + // Locate the title's baseline. + val titleBaseline = + if (titlePlaceable[LastBaseline] != AlignmentLine.Unspecified) { + titlePlaceable[LastBaseline] + } else { + 0 + } + + val layoutHeight = heightPx.roundToInt() + + layout(constraints.maxWidth, layoutHeight) { + // Navigation icon + navigationIconPlaceable.placeRelative( + x = 0, + y = (layoutHeight - navigationIconPlaceable.height) / 2 + ) + + // Title + titlePlaceable.placeRelative( + x = when (titleHorizontalArrangement) { + Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2 + Arrangement.End -> + constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width + // Arrangement.Start. + // A TopAppBarTitleInset will make sure the title is offset in case the + // navigation icon is missing. + else -> max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width) + }, + y = when (titleVerticalArrangement) { + Arrangement.Center -> (layoutHeight - titlePlaceable.height) / 2 + // Apply bottom padding from the title's baseline only when the Arrangement is + // "Bottom". + Arrangement.Bottom -> + if (titleBottomPadding == 0) layoutHeight - titlePlaceable.height + else layoutHeight - titlePlaceable.height - max( + 0, + titleBottomPadding - titlePlaceable.height + titleBaseline + ) + // Arrangement.Top + else -> 0 + } + ) + + // Action icons + actionIconsPlaceable.placeRelative( + x = constraints.maxWidth - actionIconsPlaceable.width, + y = (layoutHeight - actionIconsPlaceable.height) / 2 + ) + } + } +} + + +/** + * Settles the app bar by flinging, in case the given velocity is greater than zero, and snapping + * after the fling settles. + */ +@OptIn(ExperimentalMaterial3Api::class) +private suspend fun settleAppBar( + state: TopAppBarState, + velocity: Float, + flingAnimationSpec: DecayAnimationSpec<Float>?, + snapAnimationSpec: AnimationSpec<Float>? +): Velocity { + // Check if the app bar is completely collapsed/expanded. If so, no need to settle the app bar, + // and just return Zero Velocity. + // Note that we don't check for 0f due to float precision with the collapsedFraction + // calculation. + if (state.collapsedFraction < 0.01f || state.collapsedFraction == 1f) { + return Velocity.Zero + } + var remainingVelocity = velocity + // In case there is an initial velocity that was left after a previous user fling, animate to + // continue the motion to expand or collapse the app bar. + if (flingAnimationSpec != null && abs(velocity) > 1f) { + var lastValue = 0f + AnimationState( + initialValue = 0f, + initialVelocity = velocity, + ) + .animateDecay(flingAnimationSpec) { + val delta = value - lastValue + val initialHeightOffset = state.heightOffset + state.heightOffset = initialHeightOffset + delta + val consumed = abs(initialHeightOffset - state.heightOffset) + lastValue = value + remainingVelocity = this.velocity + // avoid rounding errors and stop if anything is unconsumed + if (abs(delta - consumed) > 0.5f) this.cancelAnimation() + } + } + // Snap if animation specs were provided. + if (snapAnimationSpec != null) { + if (state.heightOffset < 0 && + state.heightOffset > state.heightOffsetLimit + ) { + AnimationState(initialValue = state.heightOffset).animateTo( + if (state.collapsedFraction < 0.5f) { + 0f + } else { + state.heightOffsetLimit + }, + animationSpec = snapAnimationSpec + ) { state.heightOffset = value } + } + } + + return Velocity(0f, remainingVelocity) +} + +// An easing function used to compute the alpha value that is applied to the top title part of a +// Medium or Large app bar. +private val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f) + +private val ContainerHeight = 56.dp +private val LargeTitleBottomPadding = 28.dp +private val TopAppBarHorizontalPadding = 4.dp + +// A title inset when the App-Bar is a Medium or Large one. Also used to size a spacer when the +// navigation icon is missing. +private val TopAppBarTitleInset = 16.dp - TopAppBarHorizontalPadding diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt index efc623af9cc0..b4852e43adce 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt @@ -24,17 +24,14 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults -import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -136,7 +133,6 @@ private fun SearchableTopAppBar( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun SearchTopAppBar( query: TextFieldValue, @@ -144,20 +140,16 @@ private fun SearchTopAppBar( onClose: () -> Unit, actions: @Composable RowScope.() -> Unit = {}, ) { - Surface(color = SettingsTheme.colorScheme.surfaceHeader) { - TopAppBar( - title = { SearchBox(query, onQueryChange) }, - modifier = Modifier.statusBarsPadding(), - navigationIcon = { CollapseAction(onClose) }, - actions = { - if (query.text.isNotEmpty()) { - ClearAction { onQueryChange(TextFieldValue()) } - } - actions() - }, - colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = Color.Transparent), - ) - } + CustomizedTopAppBar( + title = { SearchBox(query, onQueryChange) }, + navigationIcon = { CollapseAction(onClose) }, + actions = { + if (query.text.isNotEmpty()) { + ClearAction { onQueryChange(TextFieldValue()) } + } + actions() + }, + ) BackHandler { onClose() } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt index f7cb035cbf93..33117923161b 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsTopAppBar.kt @@ -17,24 +17,9 @@ package com.android.settingslib.spa.widget.scaffold import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.LargeTopAppBar -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import com.android.settingslib.spa.framework.compose.horizontalValues -import com.android.settingslib.spa.framework.theme.SettingsDimension -import com.android.settingslib.spa.framework.theme.SettingsTheme -import com.android.settingslib.spa.framework.theme.rememberSettingsTypography @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -43,20 +28,12 @@ internal fun SettingsTopAppBar( scrollBehavior: TopAppBarScrollBehavior, actions: @Composable RowScope.() -> Unit, ) { - val colorScheme = MaterialTheme.colorScheme - // TODO: Remove MaterialTheme() after top app bar color fixed in AndroidX. - MaterialTheme( - colorScheme = remember { colorScheme.copy(surface = colorScheme.background) }, - typography = rememberSettingsTypography(), - ) { - LargeTopAppBar( - title = { Title(title) }, - navigationIcon = { NavigateBack() }, - actions = actions, - colors = largeTopAppBarColors(), - scrollBehavior = scrollBehavior, - ) - } + CustomizedLargeTopAppBar( + title = title, + navigationIcon = { NavigateBack() }, + actions = actions, + scrollBehavior = scrollBehavior, + ) } @OptIn(ExperimentalMaterial3Api::class) @@ -65,22 +42,3 @@ internal fun TopAppBarScrollBehavior.collapse() { heightOffset = heightOffsetLimit } } - -@Composable -private fun Title(title: String) { - Text( - text = title, - modifier = Modifier - .padding(WindowInsets.navigationBars.asPaddingValues().horizontalValues()) - .padding(SettingsDimension.itemPaddingAround), - overflow = TextOverflow.Ellipsis, - maxLines = 1, - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun largeTopAppBarColors() = TopAppBarDefaults.largeTopAppBarColors( - containerColor = MaterialTheme.colorScheme.background, - scrolledContainerColor = SettingsTheme.colorScheme.surfaceHeader, -) |