Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +9 −42 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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 = Loading Loading @@ -295,7 +278,7 @@ class AuthRippleController @Inject constructor( return } mView.setSensorLocation(fingerprintSensorLocation!!) mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius) showDwellRipple() } Loading @@ -307,8 +290,8 @@ class AuthRippleController @Inject constructor( private val authControllerCallback = object : AuthController.Callback { override fun onAllAuthenticatorsRegistered() { updateSensorLocation() updateUdfpsDependentParams() updateSensorLocation() } override fun onEnrollmentsChanged() { Loading @@ -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) Loading @@ -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() Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +90 −97 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 } Loading @@ -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 } Loading @@ -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() } Loading @@ -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() Loading @@ -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() Loading @@ -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() } }) Loading @@ -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 -> Loading @@ -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, Loading @@ -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 } }) Loading @@ -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) } } } packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt +6 −6 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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. Loading Loading @@ -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) } /** Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +3 −3 Original line number Diff line number Diff line Loading @@ -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()) } Loading Loading @@ -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()) } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +9 −42 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -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) { Loading Loading @@ -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 = Loading Loading @@ -295,7 +278,7 @@ class AuthRippleController @Inject constructor( return } mView.setSensorLocation(fingerprintSensorLocation!!) mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius) showDwellRipple() } Loading @@ -307,8 +290,8 @@ class AuthRippleController @Inject constructor( private val authControllerCallback = object : AuthController.Callback { override fun onAllAuthenticatorsRegistered() { updateSensorLocation() updateUdfpsDependentParams() updateSensorLocation() } override fun onEnrollmentsChanged() { Loading @@ -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) Loading @@ -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() Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +90 −97 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading @@ -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 } Loading @@ -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 } Loading @@ -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() } Loading @@ -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() Loading @@ -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() Loading @@ -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() } }) Loading @@ -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 -> Loading @@ -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, Loading @@ -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 } }) Loading @@ -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) } } }
packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt +6 −6 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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. Loading Loading @@ -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) } /** Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +3 −3 Original line number Diff line number Diff line Loading @@ -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()) } Loading Loading @@ -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()) } }