diff options
| author | 2022-12-03 02:01:52 +0000 | |
|---|---|---|
| committer | 2022-12-03 02:01:52 +0000 | |
| commit | df36fccf2c4f7a308477fd6950d515cf7d02c228 (patch) | |
| tree | 3b2fcc4d073a6cd31d9a9b824f1b03e03805b74c /packages/SystemUI/src | |
| parent | e4a1f6ce34a4278fc36076b1667355b48f34e6d8 (diff) | |
| parent | f3cb22c0cb64fda17b3037698b9e39acce4ca16b (diff) | |
Merge "Move surface effects package to AnimationLib." into tm-qpr-dev am: 31c7f600e1 am: f3cb22c0cb
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20587929
Change-Id: I48ee3d81934b4031e378fe79042748c8eebb276b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'packages/SystemUI/src')
12 files changed, 0 insertions, 1252 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt deleted file mode 100644 index 93e78acc63fd..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleController.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.surfaceeffects.ripple - -import androidx.annotation.VisibleForTesting - -/** Controller that handles playing [RippleAnimation]. */ -class MultiRippleController(private val multipleRippleView: MultiRippleView) { - - companion object { - /** Max number of ripple animations at a time. */ - @VisibleForTesting const val MAX_RIPPLE_NUMBER = 10 - } - - /** Updates all the ripple colors during the animation. */ - fun updateColor(color: Int) { - multipleRippleView.ripples.forEach { anim -> anim.updateColor(color) } - } - - fun play(rippleAnimation: RippleAnimation) { - if (multipleRippleView.ripples.size >= MAX_RIPPLE_NUMBER) { - return - } - - multipleRippleView.ripples.add(rippleAnimation) - - // Remove ripple once the animation is done - rippleAnimation.play { multipleRippleView.ripples.remove(rippleAnimation) } - - // Trigger drawing - multipleRippleView.invalidate() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt deleted file mode 100644 index f558fee776e6..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/MultiRippleView.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.surfaceeffects.ripple - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Paint -import android.util.AttributeSet -import android.util.Log -import android.view.View - -/** - * A view that allows multiple ripples to play. - * - * Use [MultiRippleController] to play ripple animations. - */ -class MultiRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { - - internal val ripples = ArrayList<RippleAnimation>() - private val listeners = ArrayList<RipplesFinishedListener>() - private val ripplePaint = Paint() - private var isWarningLogged = false - - companion object { - private const val TAG = "MultiRippleView" - - interface RipplesFinishedListener { - /** Triggered when all the ripples finish running. */ - fun onRipplesFinish() - } - } - - fun addRipplesFinishedListener(listener: RipplesFinishedListener) { - listeners.add(listener) - } - - override fun onDraw(canvas: Canvas?) { - if (canvas == null || !canvas.isHardwareAccelerated) { - // Drawing with the ripple shader requires hardware acceleration, so skip - // if it's unsupported. - if (!isWarningLogged) { - // Only log once to not spam. - Log.w( - TAG, - "Can't draw ripple shader. $canvas does not support hardware acceleration." - ) - isWarningLogged = true - } - return - } - - var shouldInvalidate = false - - ripples.forEach { anim -> - ripplePaint.shader = anim.rippleShader - canvas.drawPaint(ripplePaint) - - shouldInvalidate = shouldInvalidate || anim.isPlaying() - } - - if (shouldInvalidate) { - invalidate() - } else { // Nothing is playing. - listeners.forEach { listener -> listener.onRipplesFinish() } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt deleted file mode 100644 index b2f8994ebf51..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimation.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.surfaceeffects.ripple - -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ValueAnimator -import androidx.core.graphics.ColorUtils - -/** A single ripple animation. */ -class RippleAnimation(private val config: RippleAnimationConfig) { - internal val rippleShader: RippleShader = RippleShader(config.rippleShape) - private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f) - - init { - applyConfigToShader() - } - - /** Updates the ripple color during the animation. */ - fun updateColor(color: Int) { - config.apply { config.color = color } - applyConfigToShader() - } - - @JvmOverloads - fun play(onAnimationEnd: Runnable? = null) { - if (isPlaying()) { - return // Ignore if ripple effect is already playing - } - - animator.duration = config.duration - animator.addUpdateListener { updateListener -> - val now = updateListener.currentPlayTime - val progress = updateListener.animatedValue as Float - rippleShader.progress = progress - rippleShader.distortionStrength = if (config.shouldDistort) 1 - progress else 0f - rippleShader.time = now.toFloat() - } - animator.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - onAnimationEnd?.run() - } - } - ) - animator.start() - } - - /** Indicates whether the animation is playing. */ - fun isPlaying(): Boolean = animator.isRunning - - private fun applyConfigToShader() { - rippleShader.setCenter(config.centerX, config.centerY) - rippleShader.setMaxSize(config.maxWidth, config.maxHeight) - rippleShader.rippleFill = config.shouldFillRipple - rippleShader.pixelDensity = config.pixelDensity - rippleShader.color = ColorUtils.setAlphaComponent(config.color, config.opacity) - rippleShader.sparkleStrength = config.sparkleStrength - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt deleted file mode 100644 index 773ac55130d4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleAnimationConfig.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.android.systemui.surfaceeffects.ripple - -import android.graphics.Color - -/** - * A struct that holds the ripple animation configurations. - * - * <p>This configuration is designed to play a SINGLE animation. Do not reuse or modify the - * configuration parameters to play different animations, unless the value has to change within the - * single animation (e.g. Change color or opacity during the animation). Note that this data class - * is pulled out to make the [RippleAnimation] constructor succinct. - */ -data class RippleAnimationConfig( - val rippleShape: RippleShader.RippleShape = RippleShader.RippleShape.CIRCLE, - val duration: Long = 0L, - val centerX: Float = 0f, - val centerY: Float = 0f, - val maxWidth: Float = 0f, - val maxHeight: Float = 0f, - val pixelDensity: Float = 1f, - var color: Int = Color.WHITE, - val opacity: Int = RIPPLE_DEFAULT_ALPHA, - val shouldFillRipple: Boolean = false, - val sparkleStrength: Float = RIPPLE_SPARKLE_STRENGTH, - val shouldDistort: Boolean = true -) { - companion object { - const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f - const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt() - const val RIPPLE_DEFAULT_ALPHA: Int = 115 // full opacity is 255. - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt deleted file mode 100644 index a950d34513ee..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.ripple - -import android.graphics.PointF -import android.graphics.RuntimeShader -import android.util.MathUtils -import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary -import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary - -/** - * Shader class that renders an expanding ripple effect. The ripple contains three elements: - * - * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away - * 2. an expanding ring that appears throughout the effect - * 3. an expanding ring-shaped area that reveals noise over #2. - * - * The ripple shader will be default to the circle shape if not specified. - * - * Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java. - */ -class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) : - RuntimeShader(buildShader(rippleShape)) { - - /** Shapes that the [RippleShader] supports. */ - enum class RippleShape { - CIRCLE, - ROUNDED_BOX, - ELLIPSE - } - // language=AGSL - companion object { - private const val SHADER_UNIFORMS = - """ - uniform vec2 in_center; - uniform vec2 in_size; - uniform float in_progress; - uniform float in_cornerRadius; - uniform float in_thickness; - uniform float in_time; - uniform float in_distort_radial; - uniform float in_distort_xy; - uniform float in_fadeSparkle; - uniform float in_fadeFill; - uniform float in_fadeRing; - uniform float in_blur; - uniform float in_pixelDensity; - layout(color) uniform vec4 in_color; - uniform float in_sparkle_strength; - """ - - private const val SHADER_CIRCLE_MAIN = - """ - vec4 main(vec2 p) { - vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy); - float radius = in_size.x * 0.5; - float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur); - float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur); - float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175) - * (1.-sparkleRing) * in_fadeSparkle; - - float rippleInsideAlpha = (1.-inside) * in_fadeFill; - float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing; - float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a; - vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha; - return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength); - } - """ - - private const val SHADER_ROUNDED_BOX_MAIN = - """ - vec4 main(vec2 p) { - float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius, - in_thickness), in_blur); - float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius), - in_blur); - float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175) - * (1.-sparkleRing) * in_fadeSparkle; - - float rippleInsideAlpha = (1.-inside) * in_fadeFill; - float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing; - float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a; - vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha; - return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength); - } - """ - - private const val SHADER_ELLIPSE_MAIN = - """ - vec4 main(vec2 p) { - vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy); - - float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur); - float inside = soften(sdEllipse(p_distorted-in_center, in_size * 1.2), in_blur); - float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175) - * (1.-sparkleRing) * in_fadeSparkle; - - float rippleInsideAlpha = (1.-inside) * in_fadeFill; - float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing; - float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * in_color.a; - vec4 ripple = vec4(in_color.rgb, 1.0) * rippleAlpha; - return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength); - } - """ - - private const val CIRCLE_SHADER = - SHADER_UNIFORMS + - ShaderUtilLibrary.SHADER_LIB + - SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + - SdfShaderLibrary.CIRCLE_SDF + - SHADER_CIRCLE_MAIN - private const val ROUNDED_BOX_SHADER = - SHADER_UNIFORMS + - ShaderUtilLibrary.SHADER_LIB + - SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + - SdfShaderLibrary.ROUNDED_BOX_SDF + - SHADER_ROUNDED_BOX_MAIN - private const val ELLIPSE_SHADER = - SHADER_UNIFORMS + - ShaderUtilLibrary.SHADER_LIB + - SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + - SdfShaderLibrary.ELLIPSE_SDF + - SHADER_ELLIPSE_MAIN - - private fun buildShader(rippleShape: RippleShape): String = - when (rippleShape) { - RippleShape.CIRCLE -> CIRCLE_SHADER - RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER - RippleShape.ELLIPSE -> ELLIPSE_SHADER - } - - 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) - } - } - - /** Sets the center position of the ripple. */ - fun setCenter(x: Float, y: Float) { - 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 - } - - /** Progress of the ripple. Float value between [0, 1]. */ - var progress: Float = 0.0f - set(value) { - field = value - setFloatUniform("in_progress", value) - val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value) - - setFloatUniform( - "in_size", - /* width= */ maxSize.x * curvedProg, - /* height= */ maxSize.y * curvedProg - ) - setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f) - // radius should not exceed width and height values. - setFloatUniform("in_cornerRadius", Math.min(maxSize.x, maxSize.y) * curvedProg) - - setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value)) - - val fadeIn = subProgress(0f, 0.1f, value) - val fadeOutNoise = subProgress(0.4f, 1f, value) - var fadeOutRipple = 0f - var fadeFill = 0f - if (!rippleFill) { - fadeFill = subProgress(0f, 0.6f, value) - fadeOutRipple = subProgress(0.3f, 1f, value) - } - setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise)) - setFloatUniform("in_fadeFill", 1 - fadeFill) - setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple)) - } - - /** Play time since the start of the effect. */ - var time: Float = 0.0f - set(value) { - field = value - setFloatUniform("in_time", value) - } - - /** A hex value representing the ripple color, in the format of ARGB */ - var color: Int = 0xffffff - set(value) { - field = value - setColorUniform("in_color", value) - } - - /** - * Noise sparkle intensity. Expected value between [0, 1]. The sparkle is white, and thus with - * strength 0 it's transparent, leaving the ripple fully smooth, while with strength 1 it's - * opaque white and looks the most grainy. - */ - var sparkleStrength: Float = 0.0f - set(value) { - field = value - setFloatUniform("in_sparkle_strength", value) - } - - /** Distortion strength of the ripple. Expected value between[0, 1]. */ - var distortionStrength: Float = 0.0f - set(value) { - field = value - setFloatUniform("in_distort_radial", 75 * progress * value) - setFloatUniform("in_distort_xy", 75 * value) - } - - var pixelDensity: Float = 1.0f - set(value) { - field = value - setFloatUniform("in_pixelDensity", value) - } - - /** - * True if the ripple should stayed filled in as it expands to give a filled-in circle effect. - * False for a ring effect. - */ - var rippleFill: Boolean = false -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt deleted file mode 100644 index ae28a8b6dc2e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.surfaceeffects.ripple - -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ValueAnimator -import android.content.Context -import android.content.res.Configuration -import android.graphics.Canvas -import android.graphics.Paint -import android.util.AttributeSet -import android.view.View -import androidx.core.graphics.ColorUtils -import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape - -/** - * A generic expanding ripple effect. - * - * Set up the shader with a desired [RippleShape] using [setupShader], [setMaxSize] and [setCenter], - * then call [startRipple] to trigger the ripple expansion. - */ -open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { - - private lateinit var rippleShader: RippleShader - lateinit var rippleShape: RippleShape - private set - - private val ripplePaint = Paint() - protected val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f) - - 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) - } - - private var centerX: Float = 0.0f - private var centerY: Float = 0.0f - fun setCenter(x: Float, y: Float) { - this.centerX = x - this.centerY = y - rippleShader.setCenter(x, y) - } - - override fun onConfigurationChanged(newConfig: Configuration?) { - rippleShader.pixelDensity = resources.displayMetrics.density - super.onConfigurationChanged(newConfig) - } - - override fun onAttachedToWindow() { - rippleShader.pixelDensity = resources.displayMetrics.density - super.onAttachedToWindow() - } - - /** Initializes the shader. Must be called before [startRipple]. */ - fun setupShader(rippleShape: RippleShape = RippleShape.CIRCLE) { - this.rippleShape = rippleShape - rippleShader = RippleShader(rippleShape) - - rippleShader.color = RippleAnimationConfig.RIPPLE_DEFAULT_COLOR - rippleShader.progress = 0f - rippleShader.sparkleStrength = RippleAnimationConfig.RIPPLE_SPARKLE_STRENGTH - rippleShader.pixelDensity = resources.displayMetrics.density - - ripplePaint.shader = rippleShader - } - - @JvmOverloads - fun startRipple(onAnimationEnd: Runnable? = null) { - if (animator.isRunning) { - return // Ignore if ripple effect is already playing - } - animator.duration = duration - animator.addUpdateListener { updateListener -> - val now = updateListener.currentPlayTime - val progress = updateListener.animatedValue as Float - rippleShader.progress = progress - rippleShader.distortionStrength = 1 - progress - rippleShader.time = now.toFloat() - invalidate() - } - animator.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - onAnimationEnd?.run() - } - } - ) - animator.start() - } - - /** - * Set the color to be used for the ripple. - * - * The alpha value of the color will be applied to the ripple. The alpha range is [0-255]. - */ - fun setColor(color: Int, alpha: Int = RippleAnimationConfig.RIPPLE_DEFAULT_ALPHA) { - rippleShader.color = ColorUtils.setAlphaComponent(color, alpha) - } - - /** - * Set whether the ripple should remain filled as the ripple expands. - * - * See [RippleShader.rippleFill]. - */ - fun setRippleFill(rippleFill: Boolean) { - rippleShader.rippleFill = rippleFill - } - - /** Set the intensity of the sparkles. */ - fun setSparkleStrength(strength: Float) { - rippleShader.sparkleStrength = strength - } - - /** Indicates whether the ripple animation is playing. */ - fun rippleInProgress(): Boolean = animator.isRunning - - override fun onDraw(canvas: Canvas?) { - if (canvas == null || !canvas.isHardwareAccelerated) { - // Drawing with the ripple shader requires hardware acceleration, so skip - // if it's unsupported. - return - } - // To reduce overdraw, we mask the effect to a circle or a rectangle that's bigger than the - // active effect area. Values here should be kept in sync with the animation implementation - // in the ripple shader. - if (rippleShape == RippleShape.CIRCLE) { - val maskRadius = - (1 - - (1 - rippleShader.progress) * - (1 - rippleShader.progress) * - (1 - rippleShader.progress)) * maxWidth - canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint) - } else { - val maskWidth = - (1 - - (1 - rippleShader.progress) * - (1 - rippleShader.progress) * - (1 - rippleShader.progress)) * maxWidth * 2 - val maskHeight = - (1 - - (1 - rippleShader.progress) * - (1 - rippleShader.progress) * - (1 - rippleShader.progress)) * maxHeight * 2 - canvas.drawRect( - /* left= */ centerX - maskWidth, - /* top= */ centerY - maskHeight, - /* right= */ centerX + maskWidth, - /* bottom= */ centerY + maskHeight, - ripplePaint - ) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt deleted file mode 100644 index 8b2f46648fef..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.shaderutil - -/** Library class that contains 2D signed distance functions. */ -class SdfShaderLibrary { - // language=AGSL - companion object { - const val CIRCLE_SDF = - """ - float sdCircle(vec2 p, float r) { - return (length(p)-r) / r; - } - - float circleRing(vec2 p, float radius) { - float thicknessHalf = radius * 0.25; - - float outerCircle = sdCircle(p, radius + thicknessHalf); - float innerCircle = sdCircle(p, radius); - - return subtract(outerCircle, innerCircle); - } - """ - - const val ROUNDED_BOX_SDF = - """ - float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) { - size *= 0.5; - cornerRadius *= 0.5; - vec2 d = abs(p)-size+cornerRadius; - - float outside = length(max(d, 0.0)); - float inside = min(max(d.x, d.y), 0.0); - - return (outside+inside-cornerRadius)/size.y; - } - - float roundedBoxRing(vec2 p, vec2 size, float cornerRadius, - float borderThickness) { - float outerRoundBox = sdRoundedBox(p, size, cornerRadius); - float innerRoundBox = sdRoundedBox(p, size - vec2(borderThickness), - cornerRadius - borderThickness); - return subtract(outerRoundBox, innerRoundBox); - } - """ - - // Used non-trigonometry parametrization and Halley's method (iterative) for root finding. - // This is more expensive than the regular circle SDF, recommend to use the circle SDF if - // possible. - const val ELLIPSE_SDF = - """float sdEllipse(vec2 p, vec2 wh) { - wh *= 0.5; - - // symmetry - (wh.x > wh.y) ? wh = wh.yx, p = abs(p.yx) : p = abs(p); - - vec2 u = wh*p, v = wh*wh; - - float U1 = u.y/2.0; float U5 = 4.0*U1; - float U2 = v.y-v.x; float U6 = 6.0*U1; - float U3 = u.x-U2; float U7 = 3.0*U3; - float U4 = u.x+U2; - - float t = 0.5; - for (int i = 0; i < 3; i ++) { - float F1 = t*(t*t*(U1*t+U3)+U4)-U1; - float F2 = t*t*(U5*t+U7)+U4; - float F3 = t*(U6*t+U7); - - t += (F1*F2)/(F1*F3-F2*F2); - } - - t = clamp(t, 0.0, 1.0); - - float d = distance(p, wh*vec2(1.0-t*t,2.0*t)/(t*t+1.0)); - d /= wh.y; - - return (dot(p/wh,p/wh)>1.0) ? d : -d; - } - - float ellipseRing(vec2 p, vec2 wh) { - vec2 thicknessHalf = wh * 0.25; - - float outerEllipse = sdEllipse(p, wh + thicknessHalf); - float innerEllipse = sdEllipse(p, wh); - - return subtract(outerEllipse, innerEllipse); - } - """ - - const val SHADER_SDF_OPERATION_LIB = - """ - float soften(float d, float blur) { - float blurHalf = blur * 0.5; - return smoothstep(-blurHalf, blurHalf, d); - } - - float subtract(float outer, float inner) { - return max(outer, -inner); - } - """ - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt deleted file mode 100644 index d78e0c153db8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.shaderutil - -/** A common utility functions that are used for computing shaders. */ -class ShaderUtilLibrary { - // language=AGSL - companion object { - const val SHADER_LIB = - """ - float triangleNoise(vec2 n) { - n = fract(n * vec2(5.3987, 5.4421)); - n += dot(n.yx, n.xy + vec2(21.5351, 14.3137)); - float xy = n.x * n.y; - // compute in [0..2[ and remap to [-1.0..1.0[ - return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0; - } - - const float PI = 3.1415926535897932384626; - - float sparkles(vec2 uv, float t) { - float n = triangleNoise(uv); - float s = 0.0; - for (float i = 0; i < 4; i += 1) { - float l = i * 0.01; - float h = l + 0.1; - float o = smoothstep(n - l, h, n); - o *= abs(sin(PI * o * (t + 0.55 * i))); - s += o; - } - return s; - } - - vec2 distort(vec2 p, float time, float distort_amount_radial, - float distort_amount_xy) { - float angle = atan(p.y, p.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; - } - - // Return range [-1, 1]. - vec3 hash(vec3 p) { - p = fract(p * vec3(.3456, .1234, .9876)); - p += dot(p, p.yxz + 43.21); - p = (p.xxy + p.yxx) * p.zyx; - return (fract(sin(p) * 4567.1234567) - .5) * 2.; - } - - // Skew factors (non-uniform). - const float SKEW = 0.3333333; // 1/3 - const float UNSKEW = 0.1666667; // 1/6 - - // Return range roughly [-1,1]. - // It's because the hash function (that returns a random gradient vector) returns - // different magnitude of vectors. Noise doesn't have to be in the precise range thus - // skipped normalize. - float simplex3d(vec3 p) { - // Skew the input coordinate, so that we get squashed cubical grid - vec3 s = floor(p + (p.x + p.y + p.z) * SKEW); - - // Unskew back - vec3 u = s - (s.x + s.y + s.z) * UNSKEW; - - // Unskewed coordinate that is relative to p, to compute the noise contribution - // based on the distance. - vec3 c0 = p - u; - - // We have six simplices (in this case tetrahedron, since we are in 3D) that we - // could possibly in. - // Here, we are finding the correct tetrahedron (simplex shape), and traverse its - // four vertices (c0..3) when computing noise contribution. - // The way we find them is by comparing c0's x,y,z values. - // For example in 2D, we can find the triangle (simplex shape in 2D) that we are in - // by comparing x and y values. i.e. x>y lower, x<y, upper triangle. - // Same applies in 3D. - // - // Below indicates the offsets (or offset directions) when c0=(x0,y0,z0) - // x0>y0>z0: (1,0,0), (1,1,0), (1,1,1) - // x0>z0>y0: (1,0,0), (1,0,1), (1,1,1) - // z0>x0>y0: (0,0,1), (1,0,1), (1,1,1) - // z0>y0>x0: (0,0,1), (0,1,1), (1,1,1) - // y0>z0>x0: (0,1,0), (0,1,1), (1,1,1) - // y0>x0>z0: (0,1,0), (1,1,0), (1,1,1) - // - // The rule is: - // * For offset1, set 1 at the max component, otherwise 0. - // * For offset2, set 0 at the min component, otherwise 1. - // * For offset3, set 1 for all. - // - // Encode x0-y0, y0-z0, z0-x0 in a vec3 - vec3 en = c0 - c0.yzx; - // Each represents whether x0>y0, y0>z0, z0>x0 - en = step(vec3(0.), en); - // en.zxy encodes z0>x0, x0>y0, y0>x0 - vec3 offset1 = en * (1. - en.zxy); // find max - vec3 offset2 = 1. - en.zxy * (1. - en); // 1-(find min) - vec3 offset3 = vec3(1.); - - vec3 c1 = c0 - offset1 + UNSKEW; - vec3 c2 = c0 - offset2 + UNSKEW * 2.; - vec3 c3 = c0 - offset3 + UNSKEW * 3.; - - // Kernel summation: dot(max(0, r^2-d^2))^4, noise contribution) - // - // First compute d^2, squared distance to the point. - vec4 w; // w = max(0, r^2 - d^2)) - w.x = dot(c0, c0); - w.y = dot(c1, c1); - w.z = dot(c2, c2); - w.w = dot(c3, c3); - - // Noise contribution should decay to zero before they cross the simplex boundary. - // Usually r^2 is 0.5 or 0.6; - // 0.5 ensures continuity but 0.6 increases the visual quality for the application - // where discontinuity isn't noticeable. - w = max(0.6 - w, 0.); - - // Noise contribution from each point. - vec4 nc; - nc.x = dot(hash(s), c0); - nc.y = dot(hash(s + offset1), c1); - nc.z = dot(hash(s + offset2), c2); - nc.w = dot(hash(s + offset3), c3); - - nc *= w*w*w*w; - - // Add all the noise contributions. - // Should multiply by the possible max contribution to adjust the range in [-1,1]. - return dot(vec4(32.), nc); - } - """ - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt deleted file mode 100644 index 5ac3aad749fc..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.turbulencenoise - -import android.graphics.BlendMode -import android.graphics.Color - -/** Turbulence noise animation configuration. */ -data class TurbulenceNoiseAnimationConfig( - /** The number of grids that is used to generate noise. */ - val gridCount: Float = DEFAULT_NOISE_GRID_COUNT, - - /** Multiplier for the noise luma matte. Increase this for brighter effects. */ - val luminosityMultiplier: Float = DEFAULT_LUMINOSITY_MULTIPLIER, - - /** - * Noise move speed variables. - * - * Its sign determines the direction; magnitude determines the speed. <ul> - * ``` - * <li> [noiseMoveSpeedX] positive: right to left; negative: left to right. - * <li> [noiseMoveSpeedY] positive: bottom to top; negative: top to bottom. - * <li> [noiseMoveSpeedZ] its sign doesn't matter much, as it moves in Z direction. Use it - * to add turbulence in place. - * ``` - * </ul> - */ - val noiseMoveSpeedX: Float = 0f, - val noiseMoveSpeedY: Float = 0f, - val noiseMoveSpeedZ: Float = DEFAULT_NOISE_SPEED_Z, - - /** Color of the effect. */ - var color: Int = DEFAULT_COLOR, - /** Background color of the effect. */ - val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR, - val opacity: Int = DEFAULT_OPACITY, - val width: Float = 0f, - val height: Float = 0f, - val duration: Float = DEFAULT_NOISE_DURATION_IN_MILLIS, - val pixelDensity: Float = 1f, - val blendMode: BlendMode = DEFAULT_BLEND_MODE, - val onAnimationEnd: Runnable? = null -) { - companion object { - const val DEFAULT_NOISE_DURATION_IN_MILLIS = 7500F - const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f - const val DEFAULT_NOISE_GRID_COUNT = 1.2f - const val DEFAULT_NOISE_SPEED_Z = 0.3f - const val DEFAULT_OPACITY = 150 // full opacity is 255. - const val DEFAULT_COLOR = Color.WHITE - const val DEFAULT_BACKGROUND_COLOR = Color.BLACK - val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt deleted file mode 100644 index 4c7e5f4c7093..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.turbulencenoise - -/** A controller that plays [TurbulenceNoiseView]. */ -class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) { - /** Updates the color of the noise. */ - fun updateNoiseColor(color: Int) { - turbulenceNoiseView.updateColor(color) - } - - // TODO: add cancel and/ or pause once design requirements become clear. - /** Plays [TurbulenceNoiseView] with the given config. */ - fun play(turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig) { - turbulenceNoiseView.play(turbulenceNoiseAnimationConfig) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt deleted file mode 100644 index 19c114d2693c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.turbulencenoise - -import android.graphics.RuntimeShader -import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary -import java.lang.Float.max - -/** Shader that renders turbulence simplex noise, with no octave. */ -class TurbulenceNoiseShader : RuntimeShader(TURBULENCE_NOISE_SHADER) { - // language=AGSL - companion object { - private const val UNIFORMS = - """ - uniform float in_gridNum; - uniform vec3 in_noiseMove; - uniform vec2 in_size; - uniform float in_aspectRatio; - uniform float in_opacity; - uniform float in_pixelDensity; - layout(color) uniform vec4 in_color; - layout(color) uniform vec4 in_backgroundColor; - """ - - private const val SHADER_LIB = - """ - float getLuminosity(vec3 c) { - return 0.3*c.r + 0.59*c.g + 0.11*c.b; - } - - vec3 maskLuminosity(vec3 dest, float lum) { - dest.rgb *= vec3(lum); - // Clip back into the legal range - dest = clamp(dest, vec3(0.), vec3(1.0)); - return dest; - } - """ - - private const val MAIN_SHADER = - """ - vec4 main(vec2 p) { - vec2 uv = p / in_size.xy; - uv.x *= in_aspectRatio; - - vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = simplex3d(noiseP) * in_opacity; - vec3 mask = maskLuminosity(in_color.rgb, luma); - vec3 color = in_backgroundColor.rgb + mask * 0.6; - - // Add dither with triangle distribution to avoid color banding. Ok to dither in the - // shader here as we are in gamma space. - float dither = triangleNoise(p * in_pixelDensity) / 255.; - - // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to - // multiply rgb with a to get the correct result. - color = (color + dither.rrr) * in_color.a; - return vec4(color, in_color.a); - } - """ - - private const val TURBULENCE_NOISE_SHADER = - ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SHADER_LIB + MAIN_SHADER - } - - /** Sets the number of grid for generating noise. */ - fun setGridCount(gridNumber: Float = 1.0f) { - setFloatUniform("in_gridNum", gridNumber) - } - - /** - * Sets the pixel density of the screen. - * - * Used it for noise dithering. - */ - fun setPixelDensity(pixelDensity: Float) { - setFloatUniform("in_pixelDensity", pixelDensity) - } - - /** Sets the noise color of the effect. */ - fun setColor(color: Int) { - setColorUniform("in_color", color) - } - - /** Sets the background color of the effect. */ - fun setBackgroundColor(color: Int) { - setColorUniform("in_backgroundColor", color) - } - - /** - * Sets the opacity to achieve fade in/ out of the animation. - * - * Expected value range is [1, 0]. - */ - fun setOpacity(opacity: Float) { - setFloatUniform("in_opacity", opacity) - } - - /** Sets the size of the shader. */ - fun setSize(width: Float, height: Float) { - setFloatUniform("in_size", width, height) - setFloatUniform("in_aspectRatio", width / max(height, 0.001f)) - } - - /** Sets noise move speed in x, y, and z direction. */ - fun setNoiseMove(x: Float, y: Float, z: Float) { - setFloatUniform("in_noiseMove", x, y, z) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt deleted file mode 100644 index 8649d5924587..000000000000 --- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.surfaceeffects.turbulencenoise - -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.ValueAnimator -import android.content.Context -import android.graphics.Canvas -import android.graphics.Paint -import android.util.AttributeSet -import android.view.View -import androidx.annotation.VisibleForTesting -import androidx.core.graphics.ColorUtils -import java.util.Random -import kotlin.math.sin - -/** View that renders turbulence noise effect. */ -class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { - - companion object { - private const val MS_TO_SEC = 0.001f - private const val TWO_PI = Math.PI.toFloat() * 2f - } - - @VisibleForTesting val turbulenceNoiseShader = TurbulenceNoiseShader() - private val paint = Paint().apply { this.shader = turbulenceNoiseShader } - private val random = Random() - private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f) - private var config: TurbulenceNoiseAnimationConfig? = null - - val isPlaying: Boolean - get() = animator.isRunning - - init { - // Only visible during the animation. - visibility = INVISIBLE - } - - /** Updates the color during the animation. No-op if there's no animation playing. */ - fun updateColor(color: Int) { - config?.let { - it.color = color - applyConfig(it) - } - } - - override fun onDraw(canvas: Canvas?) { - if (canvas == null || !canvas.isHardwareAccelerated) { - // Drawing with the turbulence noise shader requires hardware acceleration, so skip - // if it's unsupported. - return - } - - canvas.drawPaint(paint) - } - - internal fun play(config: TurbulenceNoiseAnimationConfig) { - if (isPlaying) { - return // Ignore if the animation is playing. - } - visibility = VISIBLE - applyConfig(config) - - // Add random offset to avoid same patterned noise. - val offsetX = random.nextFloat() - val offsetY = random.nextFloat() - - animator.duration = config.duration.toLong() - animator.addUpdateListener { updateListener -> - val timeInSec = updateListener.currentPlayTime * MS_TO_SEC - // Remap [0,1] to [0, 2*PI] - val progress = TWO_PI * updateListener.animatedValue as Float - - turbulenceNoiseShader.setNoiseMove( - offsetX + timeInSec * config.noiseMoveSpeedX, - offsetY + timeInSec * config.noiseMoveSpeedY, - timeInSec * config.noiseMoveSpeedZ - ) - - // Fade in and out the noise as the animation progress. - // TODO: replace it with a better curve - turbulenceNoiseShader.setOpacity(sin(TWO_PI - progress) * config.luminosityMultiplier) - - invalidate() - } - - animator.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - visibility = INVISIBLE - config.onAnimationEnd?.run() - } - } - ) - animator.start() - } - - private fun applyConfig(config: TurbulenceNoiseAnimationConfig) { - this.config = config - with(turbulenceNoiseShader) { - setGridCount(config.gridCount) - setColor(ColorUtils.setAlphaComponent(config.color, config.opacity)) - setBackgroundColor(config.backgroundColor) - setSize(config.width, config.height) - setPixelDensity(config.pixelDensity) - } - paint.blendMode = config.blendMode - } -} |