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

Commit ddbd217c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Magic action button animation" into main

parents 2802b72f 0d7e0b24
Loading
Loading
Loading
Loading
+79 −45
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.notification.row

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.BlendMode
import android.graphics.Canvas
@@ -38,8 +39,8 @@ import androidx.compose.ui.viewinterop.AndroidView
import com.android.internal.graphics.ColorUtils
import com.android.systemui.res.R
import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
import kotlin.math.max
import kotlin.math.roundToInt
import com.android.wm.shell.shared.animation.Interpolators
import kotlin.math.min

/**
 * A background style for smarter-smart-actions. The style is composed by a simplex3d noise,
@@ -48,7 +49,7 @@ import kotlin.math.roundToInt
class MagicActionBackgroundDrawable(
    context: Context,
    primaryContainer: Int? = null,
    private val seed: Float = 0f,
    seed: Float = 0f,
) : Drawable() {

    private val pixelDensity = context.resources.displayMetrics.density
@@ -60,17 +61,15 @@ class MagicActionBackgroundDrawable(
            .toFloat()
    private val buttonShape = Path()
    private val paddingVertical =
        context.resources
            .getDimensionPixelSize(R.dimen.smart_reply_button_padding_vertical)
            .toFloat()
        context.resources.getDimensionPixelSize(R.dimen.smart_action_button_icon_padding).toFloat()

    /** The color of the button background. */
    private val mainColor =
        primaryContainer
            ?: context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)

    /** Slightly dimmed down version of [mainColor] used on the simplex noise. */
    private val dimColor: Int
    /** Slightly brighter version of [mainColor] used on the simplex noise. */
    private val effectColor: Int
        get() {
            val labColor = arrayOf(0.0, 0.0, 0.0).toDoubleArray()
            ColorUtils.colorToLAB(mainColor, labColor)
@@ -78,56 +77,97 @@ class MagicActionBackgroundDrawable(
            return ColorUtils.CAMToColor(
                camColor.hue,
                camColor.chroma,
                max(0f, (labColor[0] - 20).toFloat()),
                min(100f, (labColor[0] + 10).toFloat()),
            )
        }

    private val bgShader = MagicActionBackgroundShader()
    private val bgPaint = Paint()
    private val outlinePaint = Paint()
    private val gradientAnimator =
        ValueAnimator.ofFloat(0f, 1f).apply {
            duration = 2500
            interpolator = Interpolators.LINEAR
            addUpdateListener { invalidateSelf() }
        }
    private val turbulenceAnimator =
        ValueAnimator.ofFloat(seed, seed + TURBULENCE_MOVEMENT).apply {
            duration = ANIMATION_DURATION
            interpolator = Interpolators.LINEAR
            addUpdateListener { invalidateSelf() }
            start()
        }
    private val effectFadeAnimation =
        ValueAnimator.ofFloat(0f, 1f).apply {
            duration = 1000
            startDelay = ANIMATION_DURATION - 1000L
            interpolator = Interpolators.STANDARD_DECELERATE
            addUpdateListener { invalidateSelf() }
        }

    init {
        bgShader.setColorUniform("in_color", mainColor)
        bgShader.setColorUniform("in_dimColor", dimColor)
        bgShader.setColorUniform("in_effectColor", effectColor)
        bgPaint.shader = bgShader
        outlinePaint.style = Paint.Style.STROKE
        // Stroke is doubled in width and then clipped, to avoid anti-aliasing artifacts at the edge
        // of the rectangle.
        outlinePaint.strokeWidth = outlineStrokeWidth * 2
        outlinePaint.blendMode = BlendMode.SCREEN
        outlinePaint.alpha = (255 * 0.32f).roundToInt()
        outlinePaint.alpha = OUTLINE_ALPHA

        animate()
    }

    private fun animate() {
        turbulenceAnimator.start()
        gradientAnimator.start()
        effectFadeAnimation.start()
    }

    override fun draw(canvas: Canvas) {
        updateShaders()

        // We clip instead of drawing 2 rounded rects, otherwise there will be artifacts where
        // around the button background and the outline.
        canvas.save()
        canvas.clipPath(buttonShape)
        canvas.drawPath(buttonShape, bgPaint)
        canvas.drawPath(buttonShape, outlinePaint)
        canvas.restore()
    }

        canvas.drawRect(bounds, bgPaint)
        canvas.drawRoundRect(
            bounds.left.toFloat(),
            bounds.top + paddingVertical,
            bounds.right.toFloat(),
            bounds.bottom - paddingVertical,
            cornerRadius,
            cornerRadius,
            outlinePaint,
    private fun updateShaders() {
        val effectAlpha = 1f - effectFadeAnimation.animatedValue as Float
        val turbulenceZ = turbulenceAnimator.animatedValue as Float
        bgShader.setFloatUniform("in_sparkleMove", turbulenceZ * 1000)
        bgShader.setFloatUniform("in_noiseMove", 0f, 0f, turbulenceZ)
        bgShader.setFloatUniform("in_turbulenceAlpha", effectAlpha)
        bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA * effectAlpha)
        val gradientOffset = gradientAnimator.animatedValue as Float * bounds.width()
        val outlineGradient =
            LinearGradient(
                gradientOffset + bounds.left.toFloat(),
                0f,
                gradientOffset + bounds.right.toFloat(),
                0f,
                mainColor,
                ColorUtils.setAlphaComponent(mainColor, 0),
                Shader.TileMode.MIRROR,
            )
        outlinePaint.shader = outlineGradient
    }

    override fun onBoundsChange(bounds: Rect) {
        super.onBoundsChange(bounds)

        val width = bounds.width().toFloat()
        val height = bounds.height() - paddingVertical * 2
        val height = bounds.height().toFloat()
        if (width == 0f || height == 0f) return

        bgShader.setFloatUniform("in_gridNum", NOISE_SIZE)
        bgShader.setFloatUniform("in_spkarkleAlpha", SPARKLE_ALPHA)
        bgShader.setFloatUniform("in_noiseMove", 0f, 0f, 0f)
        bgShader.setFloatUniform("in_size", width, height)
        bgShader.setFloatUniform("in_aspectRatio", width / height)
        bgShader.setFloatUniform("in_time", seed)
        bgShader.setFloatUniform("in_pixelDensity", pixelDensity)

        buttonShape.reset()
@@ -140,18 +180,6 @@ class MagicActionBackgroundDrawable(
            cornerRadius,
            Path.Direction.CW,
        )

        val outlineGradient =
            LinearGradient(
                bounds.left.toFloat(),
                0f,
                bounds.right.toFloat(),
                0f,
                mainColor,
                ColorUtils.setAlphaComponent(mainColor, 0),
                Shader.TileMode.CLAMP,
            )
        outlinePaint.shader = outlineGradient
    }

    override fun setAlpha(alpha: Int) {
@@ -168,10 +196,15 @@ class MagicActionBackgroundDrawable(

    companion object {
        /** Smoothness of the turbulence. Larger numbers yield more detail. */
        private const val NOISE_SIZE = 0.7f

        private const val NOISE_SIZE = 0.57f
        /** Strength of the sparkles overlaid on the turbulence. */
        private const val SPARKLE_ALPHA = 0.15f
        /** Alpha (0..255) of the button outline */
        private const val OUTLINE_ALPHA = 82
        /** Turbulence grid size */
        private const val TURBULENCE_MOVEMENT = 4.3f
        /** Total animation duration in millis */
        private const val ANIMATION_DURATION = 5000L
    }
}

@@ -183,24 +216,25 @@ private class MagicActionBackgroundShader : RuntimeShader(SHADER) {
            """
            uniform float in_gridNum;
            uniform vec3 in_noiseMove;
            uniform half in_sparkleMove;
            uniform vec2 in_size;
            uniform float in_aspectRatio;
            uniform half in_time;
            uniform half in_pixelDensity;
            uniform float in_turbulenceAlpha;
            uniform float in_spkarkleAlpha;
            layout(color) uniform vec4 in_color;
            layout(color) uniform vec4 in_dimColor;
            layout(color) uniform vec4 in_effectColor;
        """
        private const val MAIN_SHADER =
            """vec4 main(vec2 p) {
            vec2 uv = p / in_size.xy;
            uv.x *= in_aspectRatio;
            vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
            half luma = 1.0 - getLuminosity(half3(simplex3d(noiseP)));
            half4 turbulenceColor = mix(in_color, in_dimColor, luma);
            float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time);
            half luma = getLuminosity(half3(simplex3d(noiseP)));
            half4 turbulenceColor = mix(in_color, in_effectColor, luma * in_turbulenceAlpha);
            float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_sparkleMove);
            sparkle = min(sparkle * in_spkarkleAlpha, in_spkarkleAlpha);
            return turbulenceColor + half4(half3(sparkle), 1.0);
            return saturate(turbulenceColor + half4(sparkle));
        }
        """
        private const val SHADER = UNIFORMS + ShaderUtilLibrary.SHADER_LIB + MAIN_SHADER