Loading packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +112 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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. * Loading @@ -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]. * Loading @@ -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)) } Loading Loading @@ -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( Loading Loading @@ -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 } } } packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt +13 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +2 −2 Original line number Diff line number Diff line Loading @@ -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() Loading Loading @@ -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) } } } packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +112 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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. * Loading @@ -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]. * Loading @@ -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)) } Loading Loading @@ -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( Loading Loading @@ -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 } } }
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt +13 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +2 −2 Original line number Diff line number Diff line Loading @@ -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() Loading Loading @@ -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) } } }
packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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