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

Commit bd5e9da1 authored by Beverly Tai's avatar Beverly Tai Committed by Automerger Merge Worker
Browse files

Merge "Update dwell ripple animation" into sc-v2-dev am: 76b2a10f am: a7edd836

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16482510

Change-Id: I448daa7700c0a0f058fe61b8afe992cec5f61917
parents d9340d58 a7edd836
Loading
Loading
Loading
Loading
+9 −42
Original line number Diff line number Diff line
@@ -80,11 +80,6 @@ class AuthRippleController @Inject constructor(
    private var circleReveal: LightRevealEffect? = null

    private var udfpsController: UdfpsController? = null

    private var dwellScale = 2f
    private var expandedDwellScale = 2.5f
    private var aodDwellScale = 1.9f
    private var aodExpandedDwellScale = 2.3f
    private var udfpsRadius: Float = -1f

    override fun onInit() {
@@ -128,7 +123,7 @@ class AuthRippleController @Inject constructor(
        updateSensorLocation()
        if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
            fingerprintSensorLocation != null) {
            mView.setSensorLocation(fingerprintSensorLocation!!)
            mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
            showUnlockedRipple()
        } else if (biometricSourceType == BiometricSourceType.FACE &&
            faceSensorLocation != null) {
@@ -241,24 +236,12 @@ class AuthRippleController @Inject constructor(
    }

    private fun updateRippleColor() {
        mView.setColor(
            Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
        mView.setLockScreenColor(Utils.getColorAttrDefaultColor(sysuiContext,
                R.attr.wallpaperTextColorAccent))
    }

    private fun showDwellRipple() {
        if (statusBarStateController.isDozing) {
            mView.startDwellRipple(
                    /* startRadius */ udfpsRadius,
                    /* endRadius */ udfpsRadius * aodDwellScale,
                    /* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
                    /* isDozing */ true)
        } else {
            mView.startDwellRipple(
                    /* startRadius */ udfpsRadius,
                    /* endRadius */ udfpsRadius * dwellScale,
                    /* expandedRadius */ udfpsRadius * expandedDwellScale,
                    /* isDozing */ false)
        }
        mView.startDwellRipple(statusBarStateController.isDozing)
    }

    private val keyguardUpdateMonitorCallback =
@@ -295,7 +278,7 @@ class AuthRippleController @Inject constructor(
                    return
                }

                mView.setSensorLocation(fingerprintSensorLocation!!)
                mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
                showDwellRipple()
            }

@@ -307,8 +290,8 @@ class AuthRippleController @Inject constructor(
    private val authControllerCallback =
        object : AuthController.Callback {
            override fun onAllAuthenticatorsRegistered() {
                updateSensorLocation()
                updateUdfpsDependentParams()
                updateSensorLocation()
            }

            override fun onEnrollmentsChanged() {
@@ -329,20 +312,6 @@ class AuthRippleController @Inject constructor(
    }

    inner class AuthRippleCommand : Command {
        fun printLockScreenDwellInfo(pw: PrintWriter) {
            pw.println("lock screen dwell ripple: " +
                    "\n\tsensorLocation=$fingerprintSensorLocation" +
                    "\n\tdwellScale=$dwellScale" +
                    "\n\tdwellExpand=$expandedDwellScale")
        }

        fun printAodDwellInfo(pw: PrintWriter) {
            pw.println("aod dwell ripple: " +
                    "\n\tsensorLocation=$fingerprintSensorLocation" +
                    "\n\tdwellScale=$aodDwellScale" +
                    "\n\tdwellExpand=$aodExpandedDwellScale")
        }

        override fun execute(pw: PrintWriter, args: List<String>) {
            if (args.isEmpty()) {
                invalidCommand(pw)
@@ -350,11 +319,9 @@ class AuthRippleController @Inject constructor(
                when (args[0]) {
                    "dwell" -> {
                        showDwellRipple()
                        if (statusBarStateController.isDozing) {
                            printAodDwellInfo(pw)
                        } else {
                            printLockScreenDwellInfo(pw)
                        }
                        pw.println("lock screen dwell ripple: " +
                                "\n\tsensorLocation=$fingerprintSensorLocation" +
                                "\n\tudfpsRadius=$udfpsRadius")
                    }
                    "fingerprint" -> {
                        updateSensorLocation()
+90 −97
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PointF
import android.util.AttributeSet
@@ -28,6 +29,7 @@ import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
import com.android.systemui.statusbar.charging.DwellRippleShader
import com.android.systemui.statusbar.charging.RippleShader

private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
@@ -43,23 +45,32 @@ private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)

    private val dwellPulseDuration = 50L
    private val dwellAlphaDuration = dwellPulseDuration
    private val dwellAlpha: Float = 1f
    private val dwellExpandDuration = 1200L - dwellPulseDuration
    private val dwellPulseDuration = 100L
    private val dwellExpandDuration = 2000L - dwellPulseDuration

    private val aodDwellPulseDuration = 50L
    private var aodDwellAlphaDuration = aodDwellPulseDuration
    private var aodDwellAlpha: Float = .8f
    private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
    private var drawDwell: Boolean = false
    private var drawRipple: Boolean = false

    private var lockScreenColorVal = Color.WHITE
    private val retractDuration = 400L
    private var alphaInDuration: Long = 0
    private var unlockedRippleInProgress: Boolean = false
    private val dwellShader = DwellRippleShader()
    private val dwellPaint = Paint()
    private val rippleShader = RippleShader()
    private val ripplePaint = Paint()
    private var retractAnimator: Animator? = null
    private var dwellPulseOutAnimator: Animator? = null
    private var dwellRadius: Float = 0f
        set(value) {
            dwellShader.maxRadius = value
            field = value
        }
    private var dwellOrigin: PointF = PointF()
        set(value) {
            dwellShader.origin = value
            field = value
        }
    private var radius: Float = 0f
        set(value) {
            rippleShader.radius = value
@@ -76,6 +87,11 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        rippleShader.progress = 0f
        rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
        ripplePaint.shader = rippleShader

        dwellShader.color = 0xffffffff.toInt() // default color
        dwellShader.progress = 0f
        dwellShader.distortionStrength = .4f
        dwellPaint.shader = dwellShader
        visibility = GONE
    }

@@ -84,6 +100,13 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
    }

    fun setFingerprintSensorLocation(location: PointF, sensorRadius: Float) {
        origin = location
        radius = maxOf(location.x, location.y, width - location.x, height - location.y).toFloat()
        dwellOrigin = location
        dwellRadius = sensorRadius * 1.5f
    }

    fun setAlphaInDuration(duration: Long) {
        alphaInDuration = duration
    }
@@ -97,14 +120,14 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        }

        if (dwellPulseOutAnimator?.isRunning == true) {
            val retractRippleAnimator = ValueAnimator.ofFloat(rippleShader.progress, 0f)
            val retractRippleAnimator = ValueAnimator.ofFloat(dwellShader.progress, 0f)
                    .apply {
                interpolator = retractInterpolator
                duration = retractDuration
                addUpdateListener { animator ->
                    val now = animator.currentPlayTime
                    rippleShader.progress = animator.animatedValue as Float
                    rippleShader.time = now.toFloat()
                    dwellShader.progress = animator.animatedValue as Float
                    dwellShader.time = now.toFloat()

                    invalidate()
                }
@@ -114,8 +137,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
                interpolator = Interpolators.LINEAR
                duration = retractDuration
                addUpdateListener { animator ->
                    rippleShader.color = ColorUtils.setAlphaComponent(
                            rippleShader.color,
                    dwellShader.color = ColorUtils.setAlphaComponent(
                            dwellShader.color,
                            animator.animatedValue as Int
                    )
                    invalidate()
@@ -127,13 +150,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
                addListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationStart(animation: Animator?) {
                        dwellPulseOutAnimator?.cancel()
                        rippleShader.shouldFadeOutRipple = false
                        visibility = VISIBLE
                        drawDwell = true
                    }

                    override fun onAnimationEnd(animation: Animator?) {
                        visibility = GONE
                        resetRippleAlpha()
                        drawDwell = false
                        resetDwellAlpha()
                    }
                })
                start()
@@ -142,101 +164,54 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
    }

    /**
     * Ripple that moves animates from an outer ripple ring of
     *      startRadius => endRadius => expandedRadius
     * Plays a ripple animation that grows to the dwellRadius with distortion.
     */
    fun startDwellRipple(
        startRadius: Float,
        endRadius: Float,
        expandedRadius: Float,
        isDozing: Boolean
    ) {
    fun startDwellRipple(isDozing: Boolean) {
        if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
            return
        }

        // we divide by 4 because the desired startRadius and endRadius is for the ripple's outer
        // ring see RippleShader
        val startDwellProgress = startRadius / radius / 4f
        val endInitialDwellProgress = endRadius / radius / 4f
        val endExpandDwellProgress = expandedRadius / radius / 4f
        updateDwellRippleColor(isDozing)

        val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
        val pulseOutEndAlpha = (255 * alpha).toInt()
        val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
        val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
                endInitialDwellProgress).apply {
            interpolator = Interpolators.LINEAR_OUT_SLOW_IN
            duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
        val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(0f, .8f).apply {
            interpolator = Interpolators.LINEAR
            duration = dwellPulseDuration
            addUpdateListener { animator ->
                val now = animator.currentPlayTime
                rippleShader.progress = animator.animatedValue as Float
                rippleShader.time = now.toFloat()

                invalidate()
            }
        }
                dwellShader.progress = animator.animatedValue as Float
                dwellShader.time = now.toFloat()

        val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
            interpolator = Interpolators.LINEAR
            duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
            addUpdateListener { animator ->
                rippleShader.color = ColorUtils.setAlphaComponent(
                        rippleShader.color,
                        animator.animatedValue as Int
                )
                invalidate()
            }
        }

        // slowly animate outwards until we receive a call to retractRipple or startUnlockedRipple
        val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
                endExpandDwellProgress).apply {
        val expandDwellRippleAnimator = ValueAnimator.ofFloat(.8f, 1f).apply {
            interpolator = Interpolators.LINEAR_OUT_SLOW_IN
            duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
            duration = dwellExpandDuration
            addUpdateListener { animator ->
                val now = animator.currentPlayTime
                rippleShader.progress = animator.animatedValue as Float
                rippleShader.time = now.toFloat()

                invalidate()
            }
        }
                dwellShader.progress = animator.animatedValue as Float
                dwellShader.time = now.toFloat()

        val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
                .apply {
            interpolator = Interpolators.LINEAR
            duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
            addUpdateListener { animator ->
                rippleShader.color = ColorUtils.setAlphaComponent(
                        rippleShader.color,
                        animator.animatedValue as Int
                )
                invalidate()
            }
        }

        val initialDwellPulseOutAnimator = AnimatorSet().apply {
            playTogether(dwellPulseOutRippleAnimator, dwellPulseOutAlphaAnimator)
        }
        val expandDwellAnimator = AnimatorSet().apply {
            playTogether(expandDwellRippleAnimator, expandDwellAlphaAnimator)
        }

        dwellPulseOutAnimator = AnimatorSet().apply {
            playSequentially(
                    initialDwellPulseOutAnimator,
                    expandDwellAnimator
                    dwellPulseOutRippleAnimator,
                    expandDwellRippleAnimator
            )
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animation: Animator?) {
                    retractAnimator?.cancel()
                    rippleShader.shouldFadeOutRipple = false
                    visibility = VISIBLE
                    drawDwell = true
                }

                override fun onAnimationEnd(animation: Animator?) {
                    visibility = GONE
                    drawDwell = false
                    resetRippleAlpha()
                }
            })
@@ -252,16 +227,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
            return // Ignore if ripple effect is already playing
        }

        var rippleStart = 0f
        var alphaDuration = alphaInDuration
        if (dwellPulseOutAnimator?.isRunning == true || retractAnimator?.isRunning == true) {
            rippleStart = rippleShader.progress
            alphaDuration = 0
            dwellPulseOutAnimator?.cancel()
            retractAnimator?.cancel()
        }

        val rippleAnimator = ValueAnimator.ofFloat(rippleStart, 1f).apply {
        val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
            interpolator = Interpolators.LINEAR_OUT_SLOW_IN
            duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
            addUpdateListener { animator ->
@@ -274,7 +240,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        }

        val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
            duration = alphaDuration
            duration = alphaInDuration
            addUpdateListener { animator ->
                rippleShader.color = ColorUtils.setAlphaComponent(
                    rippleShader.color,
@@ -293,12 +259,14 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
                override fun onAnimationStart(animation: Animator?) {
                    unlockedRippleInProgress = true
                    rippleShader.shouldFadeOutRipple = true
                    drawRipple = true
                    visibility = VISIBLE
                }

                override fun onAnimationEnd(animation: Animator?) {
                    onAnimationEnd?.run()
                    unlockedRippleInProgress = false
                    drawRipple = false
                    visibility = GONE
                }
            })
@@ -313,17 +281,42 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
        )
    }

    fun setColor(color: Int) {
        rippleShader.color = color
    fun setLockScreenColor(color: Int) {
        lockScreenColorVal = color
        rippleShader.color = lockScreenColorVal
        resetRippleAlpha()
    }

    fun updateDwellRippleColor(isDozing: Boolean) {
        if (isDozing) {
            dwellShader.color = Color.WHITE
        } else {
            dwellShader.color = lockScreenColorVal
        }
        resetDwellAlpha()
    }

    fun resetDwellAlpha() {
        dwellShader.color = ColorUtils.setAlphaComponent(
                dwellShader.color,
                255
        )
    }

    override fun onDraw(canvas: Canvas?) {
        // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
        // the active effect area. Values here should be kept in sync with the
        // animation implementation in the ripple shader.
        val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
        if (drawDwell) {
            val maskRadius = (1 - (1 - dwellShader.progress) * (1 - dwellShader.progress) *
                    (1 - dwellShader.progress)) * dwellRadius * 2f
            canvas?.drawCircle(dwellOrigin.x, dwellOrigin.y, maskRadius, dwellPaint)
        }

        if (drawRipple) {
            val mask = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
                    (1 - rippleShader.progress)) * radius * 2f
        canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
            canvas?.drawCircle(origin.x, origin.y, mask, ripplePaint)
        }
    }
}
+6 −6
Original line number Diff line number Diff line
@@ -59,8 +59,8 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
                }

                vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
                    return p + vec2(sin(p.x * frequency + in_phase1),
                                    cos(p.y * frequency * 1.23 + in_phase2)) * distort_amount_xy;
                    return p + vec2(sin(p.y * frequency + in_phase1),
                                    cos(p.x * frequency * -1.23 + in_phase2)) * distort_amount_xy;
                }

                vec4 ripple(vec2 p, float distort_xy, float frequency) {
@@ -73,11 +73,11 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
                """
        private const val SHADER_MAIN = """vec4 main(vec2 p) {
                    vec4 color1 = ripple(p,
                        12 * in_distortion_strength, // distort_xy
                        34 * in_distortion_strength, // distort_xy
                        0.012 // frequency
                    );
                    vec4 color2 = ripple(p,
                        17.5 * in_distortion_strength, // distort_xy
                        49 * in_distortion_strength, // distort_xy
                        0.018 // frequency
                    );
                    // Alpha blend between two layers.
@@ -128,8 +128,8 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
        set(value) {
            field = value * 0.001f
            setUniform("in_time", field)
            setUniform("in_phase1", field * 2f + 0.367f)
            setUniform("in_phase2", field * 5.2f * 1.531f)
            setUniform("in_phase1", field * 3f + 0.367f)
            setUniform("in_phase2", field * 7.2f * 1.531f)
        }

    /**
+3 −3
Original line number Diff line number Diff line
@@ -131,7 +131,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
            false /* isStrongBiometric */)

        // THEN update sensor location and show ripple
        verify(rippleView).setSensorLocation(fpsLocation)
        verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
        verify(rippleView).startUnlockedRipple(any())
    }

@@ -292,10 +292,10 @@ class AuthRippleControllerTest : SysuiTestCase() {

        reset(rippleView)
        captor.value.onThemeChanged()
        verify(rippleView).setColor(ArgumentMatchers.anyInt())
        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())

        reset(rippleView)
        captor.value.onUiModeChanged()
        verify(rippleView).setColor(ArgumentMatchers.anyInt())
        verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
    }
}