Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9055e5c authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Fix performance of clip

The clip was causing recomposition during the animation. Use a lambda to
prevent that.

Test: manual, using compose profiler
Fixes: 372458452
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Change-Id: I02ecd7359a7d95c41e8476f1c55eb76595d98f1c
parent 55b183e7
Loading
Loading
Loading
Loading
+13 −25
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.QuickSettings
import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
import com.android.systemui.qs.composefragment.ui.notificationScrimClip
import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
@@ -149,20 +150,12 @@ constructor(
    private val notificationScrimClippingParams =
        object {
            var isEnabled by mutableStateOf(false)
            var leftInset by mutableStateOf(0)
            var rightInset by mutableStateOf(0)
            var top by mutableStateOf(0)
            var bottom by mutableStateOf(0)
            var radius by mutableStateOf(0)
            var params by mutableStateOf(NotificationScrimClipParams())

            fun dump(pw: IndentingPrintWriter) {
                pw.printSection("NotificationScrimClippingParams") {
                    pw.println("isEnabled", isEnabled)
                    pw.println("leftInset", "${leftInset}px")
                    pw.println("rightInset", "${rightInset}px")
                    pw.println("top", "${top}px")
                    pw.println("bottom", "${bottom}px")
                    pw.println("radius", "${radius}px")
                    pw.println("params", params)
                }
            }
        }
@@ -216,7 +209,7 @@ constructor(
            FrameLayoutTouchPassthrough(
                context,
                { notificationScrimClippingParams.isEnabled },
                { notificationScrimClippingParams.top },
                { notificationScrimClippingParams.params.top },
            )
        frame.addView(
            composeView,
@@ -237,13 +230,7 @@ constructor(
                    Modifier.windowInsetsPadding(WindowInsets.navigationBars).thenIf(
                        notificationScrimClippingParams.isEnabled
                    ) {
                        Modifier.notificationScrimClip(
                            notificationScrimClippingParams.leftInset,
                            notificationScrimClippingParams.top,
                            notificationScrimClippingParams.rightInset,
                            notificationScrimClippingParams.bottom,
                            notificationScrimClippingParams.radius,
                        )
                        Modifier.notificationScrimClip { notificationScrimClippingParams.params }
                    },
            ) {
                val isEditing by
@@ -445,13 +432,14 @@ constructor(
        fullWidth: Boolean,
    ) {
        notificationScrimClippingParams.isEnabled = visible
        notificationScrimClippingParams.top = top
        notificationScrimClippingParams.bottom = bottom
        // Full width means that QS will show in the entire width allocated to it (for example
        // phone) vs. showing in a narrower column (for example, tablet portrait).
        notificationScrimClippingParams.leftInset = if (fullWidth) 0 else leftInset
        notificationScrimClippingParams.rightInset = if (fullWidth) 0 else rightInset
        notificationScrimClippingParams.radius = cornerRadius
        notificationScrimClippingParams.params =
            NotificationScrimClipParams(
                top,
                bottom,
                if (fullWidth) 0 else leftInset,
                if (fullWidth) 0 else rightInset,
                cornerRadius,
            )
    }

    override fun isFullyCollapsed(): Boolean {
+44 −58
Original line number Diff line number Diff line
@@ -31,83 +31,59 @@ import androidx.compose.ui.platform.InspectorInfo
 * ([ClipOp.Difference]) a `RoundRect(-leftInset, top, width + rightInset, bottom, radius, radius)`
 * from the QS container.
 */
fun Modifier.notificationScrimClip(
    leftInset: Int,
    top: Int,
    rightInset: Int,
    bottom: Int,
    radius: Int
): Modifier {
    return this then NotificationScrimClipElement(leftInset, top, rightInset, bottom, radius)
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
    return this then NotificationScrimClipElement(clipParams)
}

private class NotificationScrimClipNode(
    var leftInset: Float,
    var top: Float,
    var rightInset: Float,
    var bottom: Float,
    var radius: Float,
) : DrawModifierNode, Modifier.Node() {
private class NotificationScrimClipNode(var clipParams: () -> NotificationScrimClipParams) :
    DrawModifierNode, Modifier.Node() {
    private val path = Path()

    var invalidated = true
    private var lastClipParams = NotificationScrimClipParams()

    override fun ContentDrawScope.draw() {
        if (invalidated) {
        val newClipParams = clipParams()
        if (newClipParams != lastClipParams) {
            lastClipParams = newClipParams
            applyClipParams(path, lastClipParams)
        }
        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
    }

    private fun ContentDrawScope.applyClipParams(
        path: Path,
        clipParams: NotificationScrimClipParams,
    ) {
        with(clipParams) {
            path.rewind()
            path
                .asAndroidPath()
                .addRoundRect(
                    -leftInset,
                    top,
                    -leftInset.toFloat(),
                    top.toFloat(),
                    size.width + rightInset,
                    bottom,
                    radius,
                    radius,
                    android.graphics.Path.Direction.CW
                    bottom.toFloat(),
                    radius.toFloat(),
                    radius.toFloat(),
                    android.graphics.Path.Direction.CW,
                )
            invalidated = false
        }
        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
    }
}

private data class NotificationScrimClipElement(
    val leftInset: Int,
    val top: Int,
    val rightInset: Int,
    val bottom: Int,
    val radius: Int,
) : ModifierNodeElement<NotificationScrimClipNode>() {
private data class NotificationScrimClipElement(val clipParams: () -> NotificationScrimClipParams) :
    ModifierNodeElement<NotificationScrimClipNode>() {
    override fun create(): NotificationScrimClipNode {
        return NotificationScrimClipNode(
            leftInset.toFloat(),
            top.toFloat(),
            rightInset.toFloat(),
            bottom.toFloat(),
            radius.toFloat(),
        )
        return NotificationScrimClipNode(clipParams)
    }

    override fun update(node: NotificationScrimClipNode) {
        val changed =
            node.leftInset != leftInset.toFloat() ||
                node.top != top.toFloat() ||
                node.rightInset != rightInset.toFloat() ||
                node.bottom != bottom.toFloat() ||
                node.radius != radius.toFloat()
        if (changed) {
            node.leftInset = leftInset.toFloat()
            node.top = top.toFloat()
            node.rightInset = rightInset.toFloat()
            node.bottom = bottom.toFloat()
            node.radius = radius.toFloat()
            node.invalidated = true
        }
        node.clipParams = clipParams
    }

    override fun InspectorInfo.inspectableProperties() {
        name = "notificationScrimClip"
        with(clipParams()) {
            properties["leftInset"] = leftInset
            properties["top"] = top
            properties["rightInset"] = rightInset
@@ -115,3 +91,13 @@ private data class NotificationScrimClipElement(
            properties["radius"] = radius
        }
    }
}

/** Params for [notificationScrimClip]. */
data class NotificationScrimClipParams(
    val top: Int = 0,
    val bottom: Int = 0,
    val leftInset: Int = 0,
    val rightInset: Int = 0,
    val radius: Int = 0,
)