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

Commit 9ab9bde9 authored by András Kurucz's avatar András Kurucz Committed by Android (Google) Code Review
Browse files

Merge "[Dual Shade] Blur Notifications when QS is open over LS" into main

parents e3b6b4da 4c824ba3
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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>

+85 −11
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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.
     */
@@ -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) {
@@ -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;
@@ -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.
     */
+7 −0
Original line number Diff line number Diff line
@@ -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)

+5 −0
Original line number Diff line number Diff line
@@ -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)
@@ -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)
+36 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 *
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.notification.stack.ui.viewmodel

import com.android.compose.animation.scene.ContentKey
@@ -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
@@ -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