Loading packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1242,6 +1242,9 @@ <dimen name="min_window_blur_radius">1px</dimen> <dimen name="max_window_blur_radius">23px</dimen> <!-- Blur radius of the Notification Shade content (notifications, footer, shelf) --> <dimen name="max_shade_content_blur_radius">@dimen/max_window_blur_radius</dimen> <!-- Blur radius behind Notification Shade --> <dimen name="max_shade_window_blur_radius">34dp</dimen> Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +85 −11 Original line number Diff line number Diff line Loading @@ -52,6 +52,9 @@ import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RenderEffect; import android.graphics.RenderNode; import android.graphics.Shader; import android.os.Bundle; import android.os.SystemClock; import android.os.Trace; Loading Loading @@ -513,6 +516,13 @@ public class NotificationStackScrollLayout /** The clip path defining where we are NOT allowed to draw. */ private final Path mNegativeRoundedClipPath = new Path(); /** RenderNode to blur notifications which will be reused (redrawn) whenever NSSL is drawn. */ private final RenderNode mBlurNode = new RenderNode("BlurNode"); /** Radius of the blur effect applied to the content of the NSSL. */ private float mBlurRadius = 0f; @Nullable private RenderEffect mBlurEffect = null; /** * The clip Path used to clip the launching notification. This may be different * from the normal path, as the views launch animation could start clipped. Loading Loading @@ -5974,6 +5984,24 @@ public class NotificationStackScrollLayout invalidate(); } @Override public void setBlurRadius(float blurRadius) { if (mBlurRadius != blurRadius) { mBlurRadius = blurRadius; updateBlurEffect(); invalidate(); } } private void updateBlurEffect() { if (mBlurRadius > 0) { mBlurEffect = RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, Shader.TileMode.CLAMP); } else { mBlurEffect = null; } } /** * Set rounded rect clipping bounds on this view. */ Loading Loading @@ -6144,11 +6172,42 @@ public class NotificationStackScrollLayout @Override protected void dispatchDraw(@NonNull Canvas canvas) { if (mBlurEffect != null) { // reuse the cached RenderNode to blur mBlurNode.setPosition(0, 0, canvas.getWidth(), canvas.getHeight()); mBlurNode.setRenderEffect(mBlurEffect); Canvas blurCanvas = mBlurNode.beginRecording(); // draw all the children (except HUNs) on the blurred canvas super.dispatchDraw(blurCanvas); mBlurNode.endRecording(); // apply clipping to the canvas int saveCount = canvas.save(); applyClipToCanvas(canvas); // draw the blurred content to the clipped canvas canvas.drawRenderNode(mBlurNode); // restore the canvas, so it doesn't clip anymore canvas.restoreToCount(saveCount); // draw the children that were left out during the dispatchDraw phase for (int i = 0; i < getChildCount(); i++) { // TODO(b/388469101) draw these children in z-order ExpandableView child = getChildAtIndex(i); if (shouldSkipBlurForChild(child)) { super.drawChild(canvas, child, getDrawingTime()); } } } else { if (!mLaunchingNotification) { // When launching notifications, we're clipping the children individually instead of in // dispatchDraw // When launching notifications, we're clipping the children individually instead // of in dispatchDraw applyClipToCanvas(canvas); } super.dispatchDraw(canvas); } } private void applyClipToCanvas(Canvas canvas) { if (mShouldUseRoundedRectClipping) { // Let's clip rounded. // clip by the positive path if it is defined canvas.clipPath(mRoundedClipPath); } if (mShouldUseNegativeRoundedRectClipping) { Loading @@ -6156,14 +6215,21 @@ public class NotificationStackScrollLayout canvas.clipOutPath(mNegativeRoundedClipPath); } } super.dispatchDraw(canvas); } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean shouldUseClipping = mShouldUseRoundedRectClipping || mShouldUseNegativeRoundedRectClipping; if (mLaunchingNotification && shouldUseClipping) { if (mBlurEffect != null) { if (shouldSkipBlurForChild(child)) { // skip drawing this child during the regular dispatchDraw pass return false; } else { // draw the child as if nothing happened, non-blurred elements shouldn't be // affected by clipping either return super.drawChild(canvas, child, drawingTime); } } else if (mLaunchingNotification && shouldUseClipping) { // Let's clip children individually during notification launch canvas.save(); ExpandableView expandableView = (ExpandableView) child; Loading Loading @@ -6194,6 +6260,14 @@ public class NotificationStackScrollLayout } } private boolean shouldSkipBlurForChild(View child) { if (child instanceof ExpandableView row) { return row.isHeadsUpState(); } else { return false; } } /** * Calculate the total translation needed when dismissing. */ Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ interface NotificationScrollView { */ fun setNegativeClippingShape(shape: ShadeScrimShape?) /** * Sets a blur effect on the view. A radius of 0 means no blur. * * @param radius blur radius in pixels */ fun setBlurRadius(radius: Float) /** set the y position in px of the top of the stack in this view's coordinates */ fun setStackTop(stackTop: Float) Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +5 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ constructor( } } launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } } launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) } launch { viewModel.isShowingStackOnLockscreen.collectTraced { view.setShowingStackOnLockscreen(it) Loading Loading @@ -146,6 +147,10 @@ constructor( } } /** blur radius to be applied when the QS panel is fully expanded */ private val maxBlurRadius: Flow<Int> = configuration.getDimensionPixelSize(R.dimen.max_shade_content_blur_radius) /** flow of the scrim clipping radius */ private val scrimRadius: Flow<Int> get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius) Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +36 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ * */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.compose.animation.scene.ContentKey Loading Loading @@ -46,11 +48,14 @@ import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl import dagger.Lazy import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull Loading Loading @@ -193,6 +198,37 @@ constructor( val qsExpandFraction: Flow<Float> = shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction") /** Blur radius to be applied to Notifications. */ fun blurRadius(maxBlurRadius: Flow<Int>) = combine(blurFraction, maxBlurRadius) { fraction, maxRadius -> fraction * maxRadius } /** * Scale of the blur effect that should be applied to Notifications. * * 0 -> don't blur (default, removes all blur render effects) 1 -> do the full blur (apply a * render effect with the max blur radius) */ private val blurFraction: Flow<Float> = if (SceneContainerFlag.isEnabled) { shadeModeInteractor.shadeMode.flatMapLatest { shadeMode -> when (shadeMode) { ShadeMode.Dual -> combineTransform( shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion, ) { notificationShadeExpansion, qsExpansion -> if (notificationShadeExpansion == 0f) { // Blur out notifications as the QS overlay panel expands emit(qsExpansion) } } else -> flowOf(0f) } } } else { flowOf(0f) } /** Whether we should close any open notification guts. */ val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts Loading Loading
packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -1242,6 +1242,9 @@ <dimen name="min_window_blur_radius">1px</dimen> <dimen name="max_window_blur_radius">23px</dimen> <!-- Blur radius of the Notification Shade content (notifications, footer, shelf) --> <dimen name="max_shade_content_blur_radius">@dimen/max_window_blur_radius</dimen> <!-- Blur radius behind Notification Shade --> <dimen name="max_shade_window_blur_radius">34dp</dimen> Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +85 −11 Original line number Diff line number Diff line Loading @@ -52,6 +52,9 @@ import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RenderEffect; import android.graphics.RenderNode; import android.graphics.Shader; import android.os.Bundle; import android.os.SystemClock; import android.os.Trace; Loading Loading @@ -513,6 +516,13 @@ public class NotificationStackScrollLayout /** The clip path defining where we are NOT allowed to draw. */ private final Path mNegativeRoundedClipPath = new Path(); /** RenderNode to blur notifications which will be reused (redrawn) whenever NSSL is drawn. */ private final RenderNode mBlurNode = new RenderNode("BlurNode"); /** Radius of the blur effect applied to the content of the NSSL. */ private float mBlurRadius = 0f; @Nullable private RenderEffect mBlurEffect = null; /** * The clip Path used to clip the launching notification. This may be different * from the normal path, as the views launch animation could start clipped. Loading Loading @@ -5974,6 +5984,24 @@ public class NotificationStackScrollLayout invalidate(); } @Override public void setBlurRadius(float blurRadius) { if (mBlurRadius != blurRadius) { mBlurRadius = blurRadius; updateBlurEffect(); invalidate(); } } private void updateBlurEffect() { if (mBlurRadius > 0) { mBlurEffect = RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, Shader.TileMode.CLAMP); } else { mBlurEffect = null; } } /** * Set rounded rect clipping bounds on this view. */ Loading Loading @@ -6144,11 +6172,42 @@ public class NotificationStackScrollLayout @Override protected void dispatchDraw(@NonNull Canvas canvas) { if (mBlurEffect != null) { // reuse the cached RenderNode to blur mBlurNode.setPosition(0, 0, canvas.getWidth(), canvas.getHeight()); mBlurNode.setRenderEffect(mBlurEffect); Canvas blurCanvas = mBlurNode.beginRecording(); // draw all the children (except HUNs) on the blurred canvas super.dispatchDraw(blurCanvas); mBlurNode.endRecording(); // apply clipping to the canvas int saveCount = canvas.save(); applyClipToCanvas(canvas); // draw the blurred content to the clipped canvas canvas.drawRenderNode(mBlurNode); // restore the canvas, so it doesn't clip anymore canvas.restoreToCount(saveCount); // draw the children that were left out during the dispatchDraw phase for (int i = 0; i < getChildCount(); i++) { // TODO(b/388469101) draw these children in z-order ExpandableView child = getChildAtIndex(i); if (shouldSkipBlurForChild(child)) { super.drawChild(canvas, child, getDrawingTime()); } } } else { if (!mLaunchingNotification) { // When launching notifications, we're clipping the children individually instead of in // dispatchDraw // When launching notifications, we're clipping the children individually instead // of in dispatchDraw applyClipToCanvas(canvas); } super.dispatchDraw(canvas); } } private void applyClipToCanvas(Canvas canvas) { if (mShouldUseRoundedRectClipping) { // Let's clip rounded. // clip by the positive path if it is defined canvas.clipPath(mRoundedClipPath); } if (mShouldUseNegativeRoundedRectClipping) { Loading @@ -6156,14 +6215,21 @@ public class NotificationStackScrollLayout canvas.clipOutPath(mNegativeRoundedClipPath); } } super.dispatchDraw(canvas); } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean shouldUseClipping = mShouldUseRoundedRectClipping || mShouldUseNegativeRoundedRectClipping; if (mLaunchingNotification && shouldUseClipping) { if (mBlurEffect != null) { if (shouldSkipBlurForChild(child)) { // skip drawing this child during the regular dispatchDraw pass return false; } else { // draw the child as if nothing happened, non-blurred elements shouldn't be // affected by clipping either return super.drawChild(canvas, child, drawingTime); } } else if (mLaunchingNotification && shouldUseClipping) { // Let's clip children individually during notification launch canvas.save(); ExpandableView expandableView = (ExpandableView) child; Loading Loading @@ -6194,6 +6260,14 @@ public class NotificationStackScrollLayout } } private boolean shouldSkipBlurForChild(View child) { if (child instanceof ExpandableView row) { return row.isHeadsUpState(); } else { return false; } } /** * Calculate the total translation needed when dismissing. */ Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ interface NotificationScrollView { */ fun setNegativeClippingShape(shape: ShadeScrimShape?) /** * Sets a blur effect on the view. A radius of 0 means no blur. * * @param radius blur radius in pixels */ fun setBlurRadius(radius: Float) /** set the y position in px of the top of the stack in this view's coordinates */ fun setStackTop(stackTop: Float) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +5 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ constructor( } } launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } } launch { viewModel.blurRadius(maxBlurRadius).collect(view::setBlurRadius) } launch { viewModel.isShowingStackOnLockscreen.collectTraced { view.setShowingStackOnLockscreen(it) Loading Loading @@ -146,6 +147,10 @@ constructor( } } /** blur radius to be applied when the QS panel is fully expanded */ private val maxBlurRadius: Flow<Int> = configuration.getDimensionPixelSize(R.dimen.max_shade_content_blur_radius) /** flow of the scrim clipping radius */ private val scrimRadius: Flow<Int> get() = configuration.getDimensionPixelOffset(R.dimen.notification_scrim_corner_radius) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +36 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ * */ @file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.statusbar.notification.stack.ui.viewmodel import com.android.compose.animation.scene.ContentKey Loading Loading @@ -46,11 +48,14 @@ import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl import dagger.Lazy import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull Loading Loading @@ -193,6 +198,37 @@ constructor( val qsExpandFraction: Flow<Float> = shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction") /** Blur radius to be applied to Notifications. */ fun blurRadius(maxBlurRadius: Flow<Int>) = combine(blurFraction, maxBlurRadius) { fraction, maxRadius -> fraction * maxRadius } /** * Scale of the blur effect that should be applied to Notifications. * * 0 -> don't blur (default, removes all blur render effects) 1 -> do the full blur (apply a * render effect with the max blur radius) */ private val blurFraction: Flow<Float> = if (SceneContainerFlag.isEnabled) { shadeModeInteractor.shadeMode.flatMapLatest { shadeMode -> when (shadeMode) { ShadeMode.Dual -> combineTransform( shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion, ) { notificationShadeExpansion, qsExpansion -> if (notificationShadeExpansion == 0f) { // Blur out notifications as the QS overlay panel expands emit(qsExpansion) } } else -> flowOf(0f) } } } else { flowOf(0f) } /** Whether we should close any open notification guts. */ val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts Loading