Loading packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +5 −2 Original line number Diff line number Diff line Loading @@ -49,13 +49,16 @@ data class TurbulenceNoiseAnimationConfig( val opacity: Int = DEFAULT_OPACITY, val width: Float = 0f, val height: Float = 0f, val duration: Float = DEFAULT_NOISE_DURATION_IN_MILLIS, val maxDuration: Float = DEFAULT_MAX_DURATION_IN_MILLIS, val easeInDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val easeOutDuration: Float = DEFAULT_EASING_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_MAX_DURATION_IN_MILLIS = 7500f const val DEFAULT_EASING_DURATION_IN_MILLIS = 750f const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f const val DEFAULT_NOISE_GRID_COUNT = 1.2f const val DEFAULT_NOISE_SPEED_Z = 0.3f Loading packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt +95 −5 Original line number Diff line number Diff line Loading @@ -15,16 +15,106 @@ */ package com.android.systemui.surfaceeffects.turbulencenoise /** A controller that plays [TurbulenceNoiseView]. */ import android.view.View import androidx.annotation.VisibleForTesting import java.util.Random /** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */ class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) { companion object { /** * States of the turbulence noise animation. * * <p>The state is designed to be follow the order below: [AnimationState.EASE_IN], * [AnimationState.MAIN], [AnimationState.EASE_OUT]. */ enum class AnimationState { EASE_IN, MAIN, EASE_OUT, NOT_PLAYING } } private val random = Random() /** Current state of the animation. */ @VisibleForTesting var state: AnimationState = AnimationState.NOT_PLAYING set(value) { field = value if (state == AnimationState.NOT_PLAYING) { turbulenceNoiseView.visibility = View.INVISIBLE turbulenceNoiseView.clearConfig() } else { turbulenceNoiseView.visibility = View.VISIBLE } } init { turbulenceNoiseView.visibility = View.INVISIBLE } /** Updates the color of the noise. */ fun updateNoiseColor(color: Int) { if (state == AnimationState.NOT_PLAYING) { return } 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) /** * Plays [TurbulenceNoiseView] with the given config. * * <p>It plays ease-in, main, and ease-out animations in sequence. */ fun play(config: TurbulenceNoiseAnimationConfig) { if (state != AnimationState.NOT_PLAYING) { return // Ignore if any of the animation is playing. } turbulenceNoiseView.applyConfig(config) playEaseInAnimation() } // TODO(b/237282226): Support force finish. /** Finishes the main animation, which triggers the ease-out animation. */ fun finish() { if (state == AnimationState.MAIN) { turbulenceNoiseView.finish(nextAnimation = this::playEaseOutAnimation) } } private fun playEaseInAnimation() { if (state != AnimationState.NOT_PLAYING) { return } state = AnimationState.EASE_IN // Add offset to avoid repetitive noise. turbulenceNoiseView.playEaseIn( offsetX = random.nextFloat(), offsetY = random.nextFloat(), this::playMainAnimation ) } private fun playMainAnimation() { if (state != AnimationState.EASE_IN) { return } state = AnimationState.MAIN turbulenceNoiseView.play(this::playEaseOutAnimation) } private fun playEaseOutAnimation() { if (state != AnimationState.MAIN) { return } state = AnimationState.EASE_OUT turbulenceNoiseView.playEaseOut(onAnimationEnd = { state = AnimationState.NOT_PLAYING }) } } packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +13 −2 Original line number Diff line number Diff line Loading @@ -114,8 +114,19 @@ class TurbulenceNoiseShader : RuntimeShader(TURBULENCE_NOISE_SHADER) { setFloatUniform("in_aspectRatio", width / max(height, 0.001f)) } /** Sets noise move speed in x, y, and z direction. */ /** Current noise movements in x, y, and z axes. */ var noiseOffsetX: Float = 0f private set var noiseOffsetY: Float = 0f private set var noiseOffsetZ: Float = 0f private set /** Sets noise move offset in x, y, and z direction. */ fun setNoiseMove(x: Float, y: Float, z: Float) { setFloatUniform("in_noiseMove", x, y, z) noiseOffsetX = x noiseOffsetY = y noiseOffsetZ = z setFloatUniform("in_noiseMove", noiseOffsetX, noiseOffsetY, noiseOffsetZ) } } packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +149 −43 Original line number Diff line number Diff line Loading @@ -25,75 +25,162 @@ 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. */ /** * View that renders turbulence noise effect. * * <p>Use [TurbulenceNoiseController] to control the turbulence animation. If you want to make some * other turbulence noise effects, either add functionality to [TurbulenceNoiseController] or create * another controller instead of extend or modify the [TurbulenceNoiseView]. * * <p>Please keep the [TurbulenceNoiseView] (or View in general) not aware of the state. * * <p>Please avoid inheriting the View if possible. Instead, reconsider adding a controller for a * new case. */ 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 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 @VisibleForTesting var noiseConfig: TurbulenceNoiseAnimationConfig? = null @VisibleForTesting var currentAnimator: ValueAnimator? = null val isPlaying: Boolean get() = animator.isRunning 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 } init { // Only visible during the animation. visibility = INVISIBLE canvas.drawPaint(paint) } /** 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) internal fun updateColor(color: Int) { noiseConfig?.let { turbulenceNoiseShader.setColor(ColorUtils.setAlphaComponent(color, it.opacity)) } } 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. /** Plays the turbulence noise with no easing. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun play(onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! canvas.drawPaint(paint) val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.maxDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC turbulenceNoiseShader.setNoiseMove( initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) turbulenceNoiseShader.setOpacity(config.luminosityMultiplier) invalidate() } animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { currentAnimator = null onAnimationEnd?.run() } } ) animator.start() currentAnimator = animator } /** Plays the turbulence noise with linear ease-in. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun playEaseIn(offsetX: Float = 0f, offsetY: Float = 0f, onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeInDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC val progress = updateListener.animatedValue as Float turbulenceNoiseShader.setNoiseMove( offsetX + initialX + timeInSec * config.noiseMoveSpeedX, offsetY + initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) // TODO: Replace it with a better curve. turbulenceNoiseShader.setOpacity(progress * config.luminosityMultiplier) invalidate() } animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { currentAnimator = null onAnimationEnd?.run() } } ) fun play(config: TurbulenceNoiseAnimationConfig) { if (isPlaying) { return // Ignore if the animation is playing. animator.start() currentAnimator = animator } visibility = VISIBLE applyConfig(config) // Add random offset to avoid same patterned noise. val offsetX = random.nextFloat() val offsetY = random.nextFloat() /** Plays the turbulence noise with linear ease-out. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun playEaseOut(onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeOutDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ 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 val progress = updateListener.animatedValue as Float turbulenceNoiseShader.setNoiseMove( offsetX + timeInSec * config.noiseMoveSpeedX, offsetY + timeInSec * config.noiseMoveSpeedY, timeInSec * config.noiseMoveSpeedZ initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + 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) // TODO: Replace it with a better curve. turbulenceNoiseShader.setOpacity((1f - progress) * config.luminosityMultiplier) invalidate() } Loading @@ -101,16 +188,31 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { visibility = INVISIBLE config.onAnimationEnd?.run() currentAnimator = null onAnimationEnd?.run() } } ) animator.start() currentAnimator = animator } /** Finishes the current animation if playing and plays the next animation if given. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun finish(nextAnimation: Runnable? = null) { // Calling Animator#end sets the animation state back to the initial state. Using pause to // avoid visual artifacts. currentAnimator?.pause() currentAnimator = null nextAnimation?.run() } private fun applyConfig(config: TurbulenceNoiseAnimationConfig) { this.config = config /** Applies shader uniforms. Must be called before playing animation. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun applyConfig(config: TurbulenceNoiseAnimationConfig) { noiseConfig = config with(turbulenceNoiseShader) { setGridCount(config.gridCount) setColor(ColorUtils.setAlphaComponent(config.color, config.opacity)) Loading @@ -120,4 +222,8 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex } paint.blendMode = config.blendMode } internal fun clearConfig() { noiseConfig = null } } packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +3 −1 Original line number Diff line number Diff line Loading @@ -1070,7 +1070,9 @@ public class MediaControlPanel { TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY, /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(), /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(), TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS, this.getContext().getResources().getDisplayMetrics().density, BlendMode.PLUS, /* onAnimationEnd= */ null Loading Loading
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +5 −2 Original line number Diff line number Diff line Loading @@ -49,13 +49,16 @@ data class TurbulenceNoiseAnimationConfig( val opacity: Int = DEFAULT_OPACITY, val width: Float = 0f, val height: Float = 0f, val duration: Float = DEFAULT_NOISE_DURATION_IN_MILLIS, val maxDuration: Float = DEFAULT_MAX_DURATION_IN_MILLIS, val easeInDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val easeOutDuration: Float = DEFAULT_EASING_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_MAX_DURATION_IN_MILLIS = 7500f const val DEFAULT_EASING_DURATION_IN_MILLIS = 750f const val DEFAULT_LUMINOSITY_MULTIPLIER = 1f const val DEFAULT_NOISE_GRID_COUNT = 1.2f const val DEFAULT_NOISE_SPEED_Z = 0.3f Loading
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt +95 −5 Original line number Diff line number Diff line Loading @@ -15,16 +15,106 @@ */ package com.android.systemui.surfaceeffects.turbulencenoise /** A controller that plays [TurbulenceNoiseView]. */ import android.view.View import androidx.annotation.VisibleForTesting import java.util.Random /** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */ class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) { companion object { /** * States of the turbulence noise animation. * * <p>The state is designed to be follow the order below: [AnimationState.EASE_IN], * [AnimationState.MAIN], [AnimationState.EASE_OUT]. */ enum class AnimationState { EASE_IN, MAIN, EASE_OUT, NOT_PLAYING } } private val random = Random() /** Current state of the animation. */ @VisibleForTesting var state: AnimationState = AnimationState.NOT_PLAYING set(value) { field = value if (state == AnimationState.NOT_PLAYING) { turbulenceNoiseView.visibility = View.INVISIBLE turbulenceNoiseView.clearConfig() } else { turbulenceNoiseView.visibility = View.VISIBLE } } init { turbulenceNoiseView.visibility = View.INVISIBLE } /** Updates the color of the noise. */ fun updateNoiseColor(color: Int) { if (state == AnimationState.NOT_PLAYING) { return } 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) /** * Plays [TurbulenceNoiseView] with the given config. * * <p>It plays ease-in, main, and ease-out animations in sequence. */ fun play(config: TurbulenceNoiseAnimationConfig) { if (state != AnimationState.NOT_PLAYING) { return // Ignore if any of the animation is playing. } turbulenceNoiseView.applyConfig(config) playEaseInAnimation() } // TODO(b/237282226): Support force finish. /** Finishes the main animation, which triggers the ease-out animation. */ fun finish() { if (state == AnimationState.MAIN) { turbulenceNoiseView.finish(nextAnimation = this::playEaseOutAnimation) } } private fun playEaseInAnimation() { if (state != AnimationState.NOT_PLAYING) { return } state = AnimationState.EASE_IN // Add offset to avoid repetitive noise. turbulenceNoiseView.playEaseIn( offsetX = random.nextFloat(), offsetY = random.nextFloat(), this::playMainAnimation ) } private fun playMainAnimation() { if (state != AnimationState.EASE_IN) { return } state = AnimationState.MAIN turbulenceNoiseView.play(this::playEaseOutAnimation) } private fun playEaseOutAnimation() { if (state != AnimationState.MAIN) { return } state = AnimationState.EASE_OUT turbulenceNoiseView.playEaseOut(onAnimationEnd = { state = AnimationState.NOT_PLAYING }) } }
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +13 −2 Original line number Diff line number Diff line Loading @@ -114,8 +114,19 @@ class TurbulenceNoiseShader : RuntimeShader(TURBULENCE_NOISE_SHADER) { setFloatUniform("in_aspectRatio", width / max(height, 0.001f)) } /** Sets noise move speed in x, y, and z direction. */ /** Current noise movements in x, y, and z axes. */ var noiseOffsetX: Float = 0f private set var noiseOffsetY: Float = 0f private set var noiseOffsetZ: Float = 0f private set /** Sets noise move offset in x, y, and z direction. */ fun setNoiseMove(x: Float, y: Float, z: Float) { setFloatUniform("in_noiseMove", x, y, z) noiseOffsetX = x noiseOffsetY = y noiseOffsetZ = z setFloatUniform("in_noiseMove", noiseOffsetX, noiseOffsetY, noiseOffsetZ) } }
packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +149 −43 Original line number Diff line number Diff line Loading @@ -25,75 +25,162 @@ 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. */ /** * View that renders turbulence noise effect. * * <p>Use [TurbulenceNoiseController] to control the turbulence animation. If you want to make some * other turbulence noise effects, either add functionality to [TurbulenceNoiseController] or create * another controller instead of extend or modify the [TurbulenceNoiseView]. * * <p>Please keep the [TurbulenceNoiseView] (or View in general) not aware of the state. * * <p>Please avoid inheriting the View if possible. Instead, reconsider adding a controller for a * new case. */ 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 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 @VisibleForTesting var noiseConfig: TurbulenceNoiseAnimationConfig? = null @VisibleForTesting var currentAnimator: ValueAnimator? = null val isPlaying: Boolean get() = animator.isRunning 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 } init { // Only visible during the animation. visibility = INVISIBLE canvas.drawPaint(paint) } /** 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) internal fun updateColor(color: Int) { noiseConfig?.let { turbulenceNoiseShader.setColor(ColorUtils.setAlphaComponent(color, it.opacity)) } } 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. /** Plays the turbulence noise with no easing. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun play(onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! canvas.drawPaint(paint) val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.maxDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC turbulenceNoiseShader.setNoiseMove( initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) turbulenceNoiseShader.setOpacity(config.luminosityMultiplier) invalidate() } animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { currentAnimator = null onAnimationEnd?.run() } } ) animator.start() currentAnimator = animator } /** Plays the turbulence noise with linear ease-in. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun playEaseIn(offsetX: Float = 0f, offsetY: Float = 0f, onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeInDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ animator.addUpdateListener { updateListener -> val timeInSec = updateListener.currentPlayTime * MS_TO_SEC val progress = updateListener.animatedValue as Float turbulenceNoiseShader.setNoiseMove( offsetX + initialX + timeInSec * config.noiseMoveSpeedX, offsetY + initialY + timeInSec * config.noiseMoveSpeedY, initialZ + timeInSec * config.noiseMoveSpeedZ ) // TODO: Replace it with a better curve. turbulenceNoiseShader.setOpacity(progress * config.luminosityMultiplier) invalidate() } animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { currentAnimator = null onAnimationEnd?.run() } } ) fun play(config: TurbulenceNoiseAnimationConfig) { if (isPlaying) { return // Ignore if the animation is playing. animator.start() currentAnimator = animator } visibility = VISIBLE applyConfig(config) // Add random offset to avoid same patterned noise. val offsetX = random.nextFloat() val offsetY = random.nextFloat() /** Plays the turbulence noise with linear ease-out. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun playEaseOut(onAnimationEnd: Runnable? = null) { if (noiseConfig == null) { return } val config = noiseConfig!! val animator = ValueAnimator.ofFloat(0f, 1f) animator.duration = config.easeOutDuration.toLong() // Animation should start from the initial position to avoid abrupt transition. val initialX = turbulenceNoiseShader.noiseOffsetX val initialY = turbulenceNoiseShader.noiseOffsetY val initialZ = turbulenceNoiseShader.noiseOffsetZ 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 val progress = updateListener.animatedValue as Float turbulenceNoiseShader.setNoiseMove( offsetX + timeInSec * config.noiseMoveSpeedX, offsetY + timeInSec * config.noiseMoveSpeedY, timeInSec * config.noiseMoveSpeedZ initialX + timeInSec * config.noiseMoveSpeedX, initialY + timeInSec * config.noiseMoveSpeedY, initialZ + 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) // TODO: Replace it with a better curve. turbulenceNoiseShader.setOpacity((1f - progress) * config.luminosityMultiplier) invalidate() } Loading @@ -101,16 +188,31 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { visibility = INVISIBLE config.onAnimationEnd?.run() currentAnimator = null onAnimationEnd?.run() } } ) animator.start() currentAnimator = animator } /** Finishes the current animation if playing and plays the next animation if given. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun finish(nextAnimation: Runnable? = null) { // Calling Animator#end sets the animation state back to the initial state. Using pause to // avoid visual artifacts. currentAnimator?.pause() currentAnimator = null nextAnimation?.run() } private fun applyConfig(config: TurbulenceNoiseAnimationConfig) { this.config = config /** Applies shader uniforms. Must be called before playing animation. */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) fun applyConfig(config: TurbulenceNoiseAnimationConfig) { noiseConfig = config with(turbulenceNoiseShader) { setGridCount(config.gridCount) setColor(ColorUtils.setAlphaComponent(config.color, config.opacity)) Loading @@ -120,4 +222,8 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex } paint.blendMode = config.blendMode } internal fun clearConfig() { noiseConfig = null } }
packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +3 −1 Original line number Diff line number Diff line Loading @@ -1070,7 +1070,9 @@ public class MediaControlPanel { TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY, /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(), /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(), TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS, TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS, this.getContext().getResources().getDisplayMetrics().density, BlendMode.PLUS, /* onAnimationEnd= */ null Loading