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

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

Clip QS on draw in the containing FrameLayout

This avoids creating a new draw layer.

Test: manual
Bug: 389985793
Flag: com.android.systemui.qs_ui_refactor_compose_fragment

Change-Id: Ic4674e2a2c7af9d851cd06b5a164b3094eb0dd53
parent d04635b1
Loading
Loading
Loading
Loading
+63 −9
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.systemui.qs.composefragment
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Path
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
@@ -125,7 +127,6 @@ import com.android.systemui.qs.composefragment.SceneKeys.debugName
import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
import com.android.systemui.qs.composefragment.ui.GridAnchor
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.ui.toEditMode
import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
@@ -241,7 +242,7 @@ constructor(
            FrameLayoutTouchPassthrough(
                context,
                { notificationScrimClippingParams.isEnabled },
                { notificationScrimClippingParams.params.top },
                snapshotFlow { notificationScrimClippingParams.params },
                // Only allow scrolling when we are fully expanded. That way, we don't intercept
                // swipes in lockscreen (when somehow QS is receiving touches).
                { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing },
@@ -276,11 +277,6 @@ constructor(
                                    }
                                }
                                .graphicsLayer { alpha = viewModel.viewAlpha }
                                .thenIf(notificationScrimClippingParams.isEnabled) {
                                    Modifier.notificationScrimClip {
                                        notificationScrimClippingParams.params
                                    }
                                }
                                .thenIf(!Flags.notificationShadeBlur()) {
                                    Modifier.offset {
                                        IntOffset(
@@ -1061,17 +1057,75 @@ private const val EDIT_MODE_TIME_MILLIS = 500
private class FrameLayoutTouchPassthrough(
    context: Context,
    private val clippingEnabledProvider: () -> Boolean,
    private val clippingTopProvider: () -> Int,
    private val clippingParams: Flow<NotificationScrimClipParams>,
    private val canScrollForwardQs: () -> Boolean,
    private val emitMotionEventForFalsing: () -> Unit,
) : FrameLayout(context) {

    init {
        repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                clippingParams.collect { currentClipParams = it }
            }
        }
    }

    private val currentClippingPath = Path()
    private var lastWidth = -1
        set(value) {
            if (field != value) {
                field = value
                updateClippingPath()
            }
        }

    private var currentClipParams = NotificationScrimClipParams()
        set(value) {
            if (field != value) {
                field = value
                updateClippingPath()
            }
        }

    private fun updateClippingPath() {
        currentClippingPath.rewind()
        if (clippingEnabledProvider()) {
            val right = width + currentClipParams.rightInset
            val left = -currentClipParams.leftInset
            val top = currentClipParams.top
            val bottom = currentClipParams.bottom
            currentClippingPath.addRoundRect(
                left.toFloat(),
                top.toFloat(),
                right.toFloat(),
                bottom.toFloat(),
                currentClipParams.radius.toFloat(),
                currentClipParams.radius.toFloat(),
                Path.Direction.CW,
            )
        }
        invalidate()
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        lastWidth = right - left
    }

    override fun dispatchDraw(canvas: Canvas) {
        if (!currentClippingPath.isEmpty) {
            canvas.clipOutPath(currentClippingPath)
        }
        super.dispatchDraw(canvas)
    }

    override fun isTransformedTouchPointInView(
        x: Float,
        y: Float,
        child: View?,
        outLocalPoint: PointF?,
    ): Boolean {
        return if (clippingEnabledProvider() && y + translationY > clippingTopProvider()) {
        return if (clippingEnabledProvider() && y + translationY > currentClipParams.top) {
            false
        } else {
            super.isTransformedTouchPointInView(x, y, child, outLocalPoint)
+26 −0
Original line number Diff line number Diff line
@@ -16,47 +16,6 @@

package com.android.systemui.qs.composefragment.ui

import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.graphicsLayer

/**
 * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
 * ([ClipOp.Difference]) a `RoundRect(-leftInset, top, width + rightInset, bottom, radius, radius)`
 * from the QS container.
 */
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
    return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
        .drawWithContent {
            drawContent()
            val params = clipParams()
            val left = -params.leftInset.toFloat()
            val right = size.width + params.rightInset.toFloat()
            val top = params.top.toFloat()
            val bottom = params.bottom.toFloat()
            val clipSize = Size(right - left, bottom - top)
            if (!clipSize.isEmpty()) {
                clipRect {
                    drawRoundRect(
                        color = Color.Black,
                        cornerRadius = CornerRadius(params.radius.toFloat()),
                        blendMode = BlendMode.Clear,
                        topLeft = Offset(left, top),
                        size = Size(right - left, bottom - top),
                    )
                }
            }
        }
}

/** Params for [notificationScrimClip]. */
data class NotificationScrimClipParams(
    val top: Int = 0,