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

Commit fabc17ea authored by Yein Jo's avatar Yein Jo
Browse files

Support multiple sizes at specific progress t in RippleShader.

Bug: 269124200
Test: adb shell cmd statusbar docking-ripple
Test: RippleShaderTest
Change-Id: Ic56314b48f96beac95a3fac02c1dabd8f630e042
parent 160d9070
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ class RippleAnimation(private val config: RippleAnimationConfig) {
    private fun applyConfigToShader() {
        with(rippleShader) {
            setCenter(config.centerX, config.centerY)
            setMaxSize(config.maxWidth, config.maxHeight)
            rippleSize.setMaxSize(config.maxWidth, config.maxHeight)
            pixelDensity = config.pixelDensity
            color = ColorUtils.setAlphaComponent(config.color, config.opacity)
            sparkleStrength = config.sparkleStrength
+112 −20
Original line number Diff line number Diff line
@@ -15,9 +15,10 @@
 */
package com.android.systemui.surfaceeffects.ripple

import android.graphics.PointF
import android.graphics.RuntimeShader
import android.util.Log
import android.util.MathUtils
import androidx.annotation.VisibleForTesting
import com.android.systemui.animation.Interpolators
import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
@@ -44,6 +45,8 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
    }
    // language=AGSL
    companion object {
        private val TAG = RippleShader::class.simpleName

        // Default fade in/ out values. The value range is [0,1].
        const val DEFAULT_FADE_IN_START = 0f
        const val DEFAULT_FADE_OUT_END = 1f
@@ -184,13 +187,6 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
        setFloatUniform("in_center", x, y)
    }

    /** Max width of the ripple. */
    private var maxSize: PointF = PointF()
    fun setMaxSize(width: Float, height: Float) {
        maxSize.x = width
        maxSize.y = height
    }

    /**
     * Blur multipliers for the ripple.
     *
@@ -200,6 +196,9 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
    var blurStart: Float = 1.25f
    var blurEnd: Float = 0.5f

    /** Size of the ripple. */
    val rippleSize = RippleSize()

    /**
     * Linear progress of the ripple. Float value between [0, 1].
     *
@@ -220,13 +219,15 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
        set(value) {
            field = value

            currentWidth = maxSize.x * value
            currentHeight = maxSize.y * value
            setFloatUniform("in_size", currentWidth, currentHeight)
            rippleSize.update(value)

            setFloatUniform("in_thickness", maxSize.y * value * 0.5f)
            // radius should not exceed width and height values.
            setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * value)
            setFloatUniform("in_size", rippleSize.currentWidth, rippleSize.currentHeight)
            setFloatUniform("in_thickness", rippleSize.currentHeight * 0.5f)
            // Corner radius is always max of the min between the current width and height.
            setFloatUniform(
                "in_cornerRadius",
                Math.min(rippleSize.currentWidth, rippleSize.currentHeight)
            )

            setFloatUniform("in_blur", MathUtils.lerp(blurStart, blurEnd, value))
        }
@@ -275,12 +276,6 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
            setFloatUniform("in_pixelDensity", value)
        }

    var currentWidth: Float = 0f
        private set

    var currentHeight: Float = 0f
        private set

    /** Parameters that are used to fade in/ out of the sparkle ring. */
    val sparkleRingFadeParams =
        FadeParams(
@@ -353,4 +348,101 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) :
        /** The endpoint of the fade out, given that the animation goes from 0 to 1. */
        var fadeOutEnd: Float = DEFAULT_FADE_OUT_END,
    )

    /**
     * Desired size of the ripple at a point t in [progress].
     *
     * <p>Note that [progress] is curved and normalized. Below is an example usage:
     * SizeAtProgress(t= 0f, width= 0f, height= 0f), SizeAtProgress(t= 0.2f, width= 500f, height=
     * 700f), SizeAtProgress(t= 1f, width= 100f, height= 300f)
     *
     * <p>For simple ripple effects, you will want to use [setMaxSize] as it is translated into:
     * SizeAtProgress(t= 0f, width= 0f, height= 0f), SizeAtProgress(t= 1f, width= maxWidth, height=
     * maxHeight)
     */
    data class SizeAtProgress(
        /** Time t in [0,1] progress range. */
        var t: Float,
        /** Target width size of the ripple at time [t]. */
        var width: Float,
        /** Target height size of the ripple at time [t]. */
        var height: Float
    )

    /** Updates and stores the ripple size. */
    inner class RippleSize {
        @VisibleForTesting var sizes = mutableListOf<SizeAtProgress>()
        @VisibleForTesting var currentSizeIndex = 0
        @VisibleForTesting val initialSize = SizeAtProgress(0f, 0f, 0f)

        var currentWidth: Float = 0f
            private set
        var currentHeight: Float = 0f
            private set

        /**
         * Sets the max size of the ripple.
         *
         * <p>Use this if the ripple shape simply changes linearly.
         */
        fun setMaxSize(width: Float, height: Float) {
            setSizeAtProgresses(initialSize, SizeAtProgress(1f, width, height))
        }

        /**
         * Sets the list of [sizes].
         *
         * <p>Note that setting this clears the existing sizes.
         */
        fun setSizeAtProgresses(vararg sizes: SizeAtProgress) {
            // Reset everything.
            this.sizes.clear()
            currentSizeIndex = 0

            this.sizes.addAll(sizes)
            this.sizes.sortBy { it.t }
        }

        /**
         * Updates the current ripple size based on the progress.
         *
         * <p>Should be called when progress updates.
         */
        fun update(progress: Float) {
            val targetIndex = updateTargetIndex(progress)
            val prevIndex = Math.max(targetIndex - 1, 0)

            val targetSize = sizes[targetIndex]
            val prevSize = sizes[prevIndex]

            val subProgress = subProgress(prevSize.t, targetSize.t, progress)

            currentWidth = targetSize.width * subProgress + prevSize.width
            currentHeight = targetSize.height * subProgress + prevSize.height
        }

        private fun updateTargetIndex(progress: Float): Int {
            if (sizes.isEmpty()) {
                // It could be empty on init.
                if (progress > 0f) {
                    Log.e(
                        TAG,
                        "Did you forget to set the ripple size? Use [setMaxSize] or " +
                            "[setSizeAtProgresses] before playing the animation."
                    )
                }
                // If there's no size is set, we set everything to 0.
                setSizeAtProgresses(initialSize)
            }

            var candidate = sizes[currentSizeIndex]

            while (progress > candidate.t) {
                currentSizeIndex = Math.min(currentSizeIndex + 1, sizes.size - 1)
                candidate = sizes[currentSizeIndex]
            }

            return currentSizeIndex
        }
    }
}
+13 −8
Original line number Diff line number Diff line
@@ -45,12 +45,8 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a

    var duration: Long = 1750

    private var maxWidth: Float = 0.0f
    private var maxHeight: Float = 0.0f
    fun setMaxSize(maxWidth: Float, maxHeight: Float) {
        this.maxWidth = maxWidth
        this.maxHeight = maxHeight
        rippleShader.setMaxSize(maxWidth, maxHeight)
        rippleShader.rippleSize.setMaxSize(maxWidth, maxHeight)
    }

    private var centerX: Float = 0.0f
@@ -94,6 +90,15 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
        rippleShader.blurEnd = end
    }

    /**
     * Sets the list of [RippleShader.SizeAtProgress].
     *
     * <p>Note that this clears the list before it sets with the new data.
     */
    fun setSizeAtProgresses(vararg targetSizes: RippleShader.SizeAtProgress) {
        rippleShader.rippleSize.setSizeAtProgresses(*targetSizes)
    }

    @JvmOverloads
    fun startRipple(onAnimationEnd: Runnable? = null) {
        if (animator.isRunning) {
@@ -145,11 +150,11 @@ open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, a
        // active effect area. Values here should be kept in sync with the animation implementation
        // in the ripple shader. (Twice bigger)
        if (rippleShape == RippleShape.CIRCLE) {
            val maskRadius = rippleShader.currentWidth
            val maskRadius = rippleShader.rippleSize.currentWidth
            canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
        } else {
            val maskWidth = rippleShader.currentWidth * 2
            val maskHeight = rippleShader.currentHeight * 2
            val maskWidth = rippleShader.rippleSize.currentWidth * 2
            val maskHeight = rippleShader.rippleSize.currentHeight * 2
            canvas.drawRect(
                /* left= */ centerX - maskWidth,
                /* top= */ centerY - maskHeight,
+2 −2
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        }
    private var radius: Float = 0f
        set(value) {
            rippleShader.setMaxSize(value * 2f, value * 2f)
            rippleShader.rippleSize.setMaxSize(value * 2f, value * 2f)
            field = value
        }
    private var origin: Point = Point()
@@ -364,7 +364,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at

        if (drawRipple) {
            canvas?.drawCircle(origin.x.toFloat(), origin.y.toFloat(),
                    rippleShader.currentWidth, ripplePaint)
                    rippleShader.rippleSize.currentWidth, ripplePaint)
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleVi
    // Calculates the actual starting percentage according to ripple shader progress set method.
    // Check calculations in [RippleShader.progress]
    fun calculateStartingPercentage(newHeight: Float): Float {
        val ratio = rippleShader.currentHeight / newHeight
        val ratio = rippleShader.rippleSize.currentHeight / newHeight
        val remainingPercentage = (1 - ratio).toDouble().pow(1 / 3.toDouble()).toFloat()
        return 1 - remainingPercentage
    }
Loading