diff options
| author | 2025-03-10 13:17:54 +0100 | |
|---|---|---|
| committer | 2025-03-10 14:48:09 +0100 | |
| commit | 22ed296e8ef8af495e43c5e69b4ab82713f3b344 (patch) | |
| tree | 625d48591d13fb6f0d119058c3fbdd07449cafa7 | |
| parent | 2d07926f5d58605df4462404286c5792e95eb8bb (diff) | |
Rename fadingBackground to animatedBackground with () -> Color param
This CL renames fadingBackground() to animatedBackground() and makes the
color parameter be a () -> Color, so that the color can also be
animated. This will be used by Expandable so that its color value is
read during the drawing phase.
The implementation is also now based on the Modifier Node API, and is
mostly a fork of the background() modifier implementation.
Bug: 402059261
Test: FooterActionsScreenshotTest
Flag: EXEMPT simple refactoring
Change-Id: Id4751ddb690b4c94d2cb669141cd27a2eb349b56
3 files changed, 162 insertions, 116 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt new file mode 100644 index 000000000000..5b1f0a7c6eb6 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/AnimatedBackground.kt @@ -0,0 +1,159 @@ +/* + * Copyright (C) 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.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawOutline +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.node.DrawModifierNode +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.node.observeReads +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.LayoutDirection + +/** + * Draws a background in a given [shape] and with a [color] or [alpha] that can be animated. + * + * @param color color to paint background with + * @param alpha alpha of the background + * @param shape desired shape of the background + */ +fun Modifier.animatedBackground( + color: () -> Color, + alpha: () -> Float = DefaultAlpha, + shape: Shape = RectangleShape, +) = + this.then( + BackgroundElement( + color = color, + alpha = alpha, + shape = shape, + inspectorInfo = + debugInspectorInfo { + name = "background" + value = color + properties["color"] = color + properties["alpha"] = alpha + properties["shape"] = shape + }, + ) + ) + +private val DefaultAlpha = { 1f } + +private class BackgroundElement( + private val color: () -> Color, + private val alpha: () -> Float, + private val shape: Shape, + private val inspectorInfo: InspectorInfo.() -> Unit, +) : ModifierNodeElement<BackgroundNode>() { + override fun create(): BackgroundNode { + return BackgroundNode(color, alpha, shape) + } + + override fun update(node: BackgroundNode) { + node.color = color + node.alpha = alpha + node.shape = shape + } + + override fun InspectorInfo.inspectableProperties() { + inspectorInfo() + } + + override fun hashCode(): Int { + var result = color.hashCode() + result = 31 * result + alpha.hashCode() + result = 31 * result + shape.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + val otherModifier = other as? BackgroundElement ?: return false + return color == otherModifier.color && + alpha == otherModifier.alpha && + shape == otherModifier.shape + } +} + +private class BackgroundNode(var color: () -> Color, var alpha: () -> Float, var shape: Shape) : + DrawModifierNode, Modifier.Node(), ObserverModifierNode { + + // Naively cache outline calculation if input parameters are the same, we manually observe + // reads inside shape#createOutline separately + private var lastSize: Size = Size.Unspecified + private var lastLayoutDirection: LayoutDirection? = null + private var lastOutline: Outline? = null + private var lastShape: Shape? = null + private var tmpOutline: Outline? = null + + override fun ContentDrawScope.draw() { + if (shape === RectangleShape) { + // shortcut to avoid Outline calculation and allocation + drawRect() + } else { + drawOutline() + } + drawContent() + } + + override fun onObservedReadsChanged() { + // Reset cached properties + lastSize = Size.Unspecified + lastLayoutDirection = null + lastOutline = null + lastShape = null + // Invalidate draw so we build the cache again - this is needed because observeReads within + // the draw scope obscures the state reads from the draw scope's observer + invalidateDraw() + } + + private fun ContentDrawScope.drawRect() { + drawRect(color = color(), alpha = alpha()) + } + + private fun ContentDrawScope.drawOutline() { + val outline = getOutline() + drawOutline(outline, color = color(), alpha = alpha()) + } + + private fun ContentDrawScope.getOutline(): Outline { + val outline: Outline? + if (size == lastSize && layoutDirection == lastLayoutDirection && lastShape == shape) { + outline = lastOutline!! + } else { + // Manually observe reads so we can directly invalidate the outline when it changes + // Use tmpOutline to avoid creating an object reference to local var outline + observeReads { tmpOutline = shape.createOutline(size, layoutDirection, this) } + outline = tmpOutline + tmpOutline = null + } + lastOutline = outline + lastSize = size + lastLayoutDirection = layoutDirection + lastShape = shape + return outline!! + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt deleted file mode 100644 index 794b7a4a3d30..000000000000 --- a/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 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.compose.modifiers - -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.DrawModifier -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Outline -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.drawOutline -import androidx.compose.ui.graphics.drawscope.ContentDrawScope -import androidx.compose.ui.platform.InspectorInfo -import androidx.compose.ui.platform.InspectorValueInfo -import androidx.compose.ui.platform.debugInspectorInfo -import androidx.compose.ui.unit.LayoutDirection - -/** - * Draws a fading [shape] with a solid [color] and [alpha] behind the content. - * - * @param color color to paint background with - * @param alpha alpha of the background - * @param shape desired shape of the background - */ -fun Modifier.fadingBackground(color: Color, alpha: () -> Float, shape: Shape = RectangleShape) = - this.then( - FadingBackground( - brush = SolidColor(color), - alpha = alpha, - shape = shape, - inspectorInfo = - debugInspectorInfo { - name = "background" - value = color - properties["color"] = color - properties["alpha"] = alpha - properties["shape"] = shape - }, - ) - ) - -private class FadingBackground -constructor( - private val brush: Brush, - private val shape: Shape, - private val alpha: () -> Float, - inspectorInfo: InspectorInfo.() -> Unit, -) : DrawModifier, InspectorValueInfo(inspectorInfo) { - // naive cache outline calculation if size is the same - private var lastSize: Size? = null - private var lastLayoutDirection: LayoutDirection? = null - private var lastOutline: Outline? = null - - override fun ContentDrawScope.draw() { - if (shape === RectangleShape) { - // shortcut to avoid Outline calculation and allocation - drawRect() - } else { - drawOutline() - } - drawContent() - } - - private fun ContentDrawScope.drawRect() { - drawRect(brush, alpha = alpha()) - } - - private fun ContentDrawScope.drawOutline() { - val outline = - if (size == lastSize && layoutDirection == lastLayoutDirection) { - lastOutline!! - } else { - shape.createOutline(size, layoutDirection, this) - } - drawOutline(outline, brush = brush, alpha = alpha()) - lastOutline = outline - lastSize = size - lastLayoutDirection = layoutDirection - } - - override fun hashCode(): Int { - var result = brush.hashCode() - result = 31 * result + alpha.hashCode() - result = 31 * result + shape.hashCode() - return result - } - - override fun equals(other: Any?): Boolean { - val otherModifier = other as? FadingBackground ?: return false - return brush == otherModifier.brush && - alpha == otherModifier.alpha && - shape == otherModifier.shape - } - - override fun toString(): String = "FadingBackground(brush=$brush, alpha = $alpha, shape=$shape)" -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt index 216f0a74e1c7..7782705d4c61 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt @@ -73,7 +73,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.compose.animation.Expandable import com.android.compose.animation.scene.ContentScope -import com.android.compose.modifiers.fadingBackground +import com.android.compose.modifiers.animatedBackground import com.android.compose.theme.colorAttr import com.android.systemui.Flags.notificationShadeBlur import com.android.systemui.animation.Expandable @@ -172,8 +172,8 @@ fun FooterActions( val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius) val backgroundModifier = remember(backgroundColor, backgroundAlphaValue, backgroundTopRadius) { - Modifier.fadingBackground( - backgroundColor, + Modifier.animatedBackground( + { backgroundColor }, backgroundAlphaValue, RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius), ) |