Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +63 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 }, Loading Loading @@ -276,11 +277,6 @@ constructor( } } .graphicsLayer { alpha = viewModel.viewAlpha } .thenIf(notificationScrimClippingParams.isEnabled) { Modifier.notificationScrimClip { notificationScrimClippingParams.params } } .thenIf(!Flags.notificationShadeBlur()) { Modifier.offset { IntOffset( Loading Loading @@ -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) Loading packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt→packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt +26 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +63 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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 }, Loading Loading @@ -276,11 +277,6 @@ constructor( } } .graphicsLayer { alpha = viewModel.viewAlpha } .thenIf(notificationScrimClippingParams.isEnabled) { Modifier.notificationScrimClip { notificationScrimClippingParams.params } } .thenIf(!Flags.notificationShadeBlur()) { Modifier.offset { IntOffset( Loading Loading @@ -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) Loading
packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt→packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt +26 −0 Original line number Diff line number Diff line Loading @@ -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, Loading