From 3bcb2301eb24c9dfdb4726ec76c76cab44f89e80 Mon Sep 17 00:00:00 2001 From: Shan Huang Date: Thu, 25 Mar 2021 17:55:43 +0800 Subject: [PATCH] Add distortion to ripple effect. Also tweak ripple thickness and spreading speed. Test: Manual Test: SystemUITests Bug: 183382912 Change-Id: I913aabd7be0128a0eee58c1467b610c80ba641d4 --- .../systemui/biometrics/AuthRippleView.kt | 3 +- .../charging/WirelessChargingLayout.java | 2 +- .../statusbar/charging/ChargingRippleView.kt | 9 +- .../statusbar/charging/RippleShader.kt | 115 ++++++++++++------ 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index 2e321338999cc..1270677ccbc34 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -60,9 +60,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at animator.duration = RIPPLE_ANIMATION_DURATION animator.addUpdateListener { animator -> val now = animator.currentPlayTime - val phase = now / 30000f rippleShader.progress = animator.animatedValue as Float - rippleShader.noisePhase = phase + rippleShader.time = now.toFloat() invalidate() } animator.addListener(object : AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java index ce0b51489490a..5cc8299acb94b 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java +++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java @@ -42,7 +42,7 @@ import java.text.NumberFormat; */ public class WirelessChargingLayout extends FrameLayout { public static final int UNKNOWN_BATTERY_LEVEL = -1; - private static final long RIPPLE_ANIMATION_DURATION = 2000; + private static final long RIPPLE_ANIMATION_DURATION = 1133; private ChargingRippleView mRippleView; public WirelessChargingLayout(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt index 05af08e0287af..2f198d5002997 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt @@ -41,7 +41,7 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context set(value) { rippleShader.radius = value } var origin: PointF = PointF() set(value) { rippleShader.origin = value } - var duration: Long = 1500 + var duration: Long = 1750 init { rippleShader.color = defaultColor @@ -59,9 +59,10 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context animator.duration = duration animator.addUpdateListener { animator -> val now = animator.currentPlayTime - val phase = now / 30000f - rippleShader.progress = animator.animatedValue as Float - rippleShader.noisePhase = phase + val progress = animator.animatedValue as Float + rippleShader.progress = progress + rippleShader.distortionStrength = 1 - progress + rippleShader.time = now.toFloat() invalidate() } animator.addListener(object : AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt index d400205af50ba..296868a20dad9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.charging import android.graphics.Color import android.graphics.PointF import android.graphics.RuntimeShader +import android.util.MathUtils /** * Shader class that renders an expanding charging ripple effect. A charging ripple contains @@ -33,7 +34,14 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { private const val SHADER_UNIFORMS = """uniform vec2 in_origin; uniform float in_progress; uniform float in_maxRadius; - uniform float in_noisePhase; + uniform float in_time; + uniform float in_distort_radial; + uniform float in_distort_xy; + uniform float in_radius; + uniform float in_fadeSparkle; + uniform float in_fadeCircle; + uniform float in_fadeRing; + uniform float in_blur; uniform vec4 in_color; uniform float in_sparkle_strength;""" private const val SHADER_LIB = """float triangleNoise(vec2 n) { @@ -67,71 +75,97 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { } float softRing(vec2 uv, vec2 xy, float radius, float blur) { - float thickness = 0.4; - float circle_outer = softCircle(uv, xy, - radius + thickness * radius * 0.5, blur); - float circle_inner = softCircle(uv, xy, - radius - thickness * radius * 0.5, blur); + float thickness_half = radius * 0.25; + float circle_outer = softCircle(uv, xy, radius + thickness_half, blur); + float circle_inner = softCircle(uv, xy, radius - thickness_half, blur); return circle_outer - circle_inner; } - float subProgress(float start, float end, float progress) { - float sub = clamp(progress, start, end); - return (sub - start) / (end - start); - } - - float smoothstop2(float t) { - return 1 - (1 - t) * (1 - t); + vec2 distort(vec2 p, vec2 origin, float time, + float distort_amount_radial, float distort_amount_xy) { + float2 distance = origin - p; + float angle = atan(distance.y, distance.x); + return p + vec2(sin(angle * 8 + time * 0.003 + 1.641), + cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial + + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123), + cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy; }""" private const val SHADER_MAIN = """vec4 main(vec2 p) { - float fadeIn = subProgress(0., 0.1, in_progress); - float fadeOutNoise = subProgress(0.8, 1., in_progress); - float fadeOutRipple = subProgress(0.7, 1., in_progress); - float fadeCircle = subProgress(0., 0.5, in_progress); - float radius = smoothstop2(in_progress) * in_maxRadius; - float sparkleRing = softRing(p, in_origin, radius, 0.5); - float sparkleAlpha = min(fadeIn, 1. - fadeOutNoise); - float sparkle = sparkles(p, in_noisePhase) * sparkleRing * sparkleAlpha; - float circle = softCircle(p, in_origin, radius * 1.2, 0.5) - * (1 - fadeCircle); - float fadeRipple = min(fadeIn, 1.-fadeOutRipple); - float rippleAlpha = softRing(p, in_origin, radius, 0.5) - * fadeRipple * in_color.a; - vec4 ripple = in_color * max(circle, rippleAlpha) * 0.3; + vec2 p_distorted = distort(p, in_origin, in_time, in_distort_radial, + in_distort_xy); + + // Draw shapes + float sparkleRing = softRing(p_distorted, in_origin, in_radius, in_blur); + float sparkle = sparkles(p, in_time * 0.000033) * sparkleRing * in_fadeSparkle; + float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur); + float rippleAlpha = max(circle * in_fadeCircle, + softRing(p_distorted, in_origin, in_radius, in_blur) * in_fadeRing) * 0.36; + vec4 ripple = in_color * rippleAlpha; return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength); }""" private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN + + private fun subProgress(start: Float, end: Float, progress: Float): Float { + val min = Math.min(start, end) + val max = Math.max(start, end) + val sub = Math.min(Math.max(progress, min), max) + return (sub - start) / (end - start) + } } /** * Maximum radius of the ripple. */ var radius: Float = 0.0f - set(value) { setUniform("in_maxRadius", value) } + set(value) { + field = value + setUniform("in_maxRadius", value) + } /** * Origin coordinate of the ripple. */ var origin: PointF = PointF() - set(value) { setUniform("in_origin", floatArrayOf(value.x, value.y)) } + set(value) { + field = value + setUniform("in_origin", floatArrayOf(value.x, value.y)) + } /** * Progress of the ripple. Float value between [0, 1]. */ var progress: Float = 0.0f - set(value) { setUniform("in_progress", value) } + set(value) { + field = value + setUniform("in_progress", value) + setUniform("in_radius", + (1 - (1 - value) * (1 - value) * (1 - value))* 1.2f * radius) + setUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value)) + + val fadeIn = subProgress(0f, 0.1f, value) + val fadeOutNoise = subProgress(0.8f, 1f, value) + val fadeOutRipple = subProgress(0.4f, 1f, value) + val fadeCircle = subProgress(0f, 0.2f, value) + setUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise)) + setUniform("in_fadeCircle", 1 - fadeCircle) + setUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple)) + } /** - * Continuous offset used as noise phase. + * Play time since the start of the effect. */ - var noisePhase: Float = 0.0f - set(value) { setUniform("in_noisePhase", value) } + var time: Float = 0.0f + set(value) { + field = value + setUniform("in_time", value) + } /** * A hex value representing the ripple color, in the format of ARGB */ var color: Int = 0xffffff.toInt() set(value) { + field = value val color = Color.valueOf(value) setUniform("in_color", floatArrayOf(color.red(), color.green(), color.blue(), color.alpha())) @@ -143,5 +177,18 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { * it's opaque white and looks the most grainy. */ var sparkleStrength: Float = 0.0f - set(value) { setUniform("in_sparkle_strength", value) } + set(value) { + field = value + setUniform("in_sparkle_strength", value) + } + + /** + * Distortion strength of the ripple. Expected value between[0, 1]. + */ + var distortionStrength: Float = 0.0f + set(value) { + field = value + setUniform("in_distort_radial", 75 * progress * value) + setUniform("in_distort_xy", 75 * value) + } }