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

Commit 5725b93f authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Clip QS on draw in the containing FrameLayout" into main

parents 24d61b68 adefb369
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,