1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.surfaceeffects.ripple 18 19 import android.animation.Animator 20 import android.animation.AnimatorListenerAdapter 21 import android.animation.ValueAnimator 22 import androidx.annotation.VisibleForTesting 23 import androidx.core.graphics.ColorUtils 24 25 /** A single ripple animation. */ 26 class RippleAnimation(private val config: RippleAnimationConfig) { 27 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) 28 val rippleShader: RippleShader = RippleShader(config.rippleShape) 29 private val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f) 30 31 init { 32 applyConfigToShader() 33 } 34 35 /** Updates the ripple color during the animation. */ 36 fun updateColor(color: Int) { 37 config.apply { config.color = color } 38 applyConfigToShader() 39 } 40 41 @JvmOverloads 42 fun play(onAnimationEnd: Runnable? = null) { 43 if (isPlaying()) { 44 return // Ignore if ripple effect is already playing 45 } 46 47 animator.duration = config.duration 48 animator.addUpdateListener { updateListener -> 49 val now = updateListener.currentPlayTime 50 val progress = updateListener.animatedValue as Float 51 rippleShader.rawProgress = progress 52 rippleShader.distortionStrength = if (config.shouldDistort) 1 - progress else 0f 53 rippleShader.time = now.toFloat() 54 } 55 animator.addListener( 56 object : AnimatorListenerAdapter() { 57 override fun onAnimationEnd(animation: Animator) { 58 onAnimationEnd?.run() 59 } 60 } 61 ) 62 animator.start() 63 } 64 65 /** Indicates whether the animation is playing. */ 66 fun isPlaying(): Boolean = animator.isRunning 67 68 private fun applyConfigToShader() { 69 with(rippleShader) { 70 setCenter(config.centerX, config.centerY) 71 rippleSize.setMaxSize(config.maxWidth, config.maxHeight) 72 pixelDensity = config.pixelDensity 73 color = ColorUtils.setAlphaComponent(config.color, config.opacity) 74 sparkleStrength = config.sparkleStrength 75 76 assignFadeParams(baseRingFadeParams, config.baseRingFadeParams) 77 assignFadeParams(sparkleRingFadeParams, config.sparkleRingFadeParams) 78 assignFadeParams(centerFillFadeParams, config.centerFillFadeParams) 79 } 80 } 81 82 private fun assignFadeParams( 83 destFadeParams: RippleShader.FadeParams, 84 srcFadeParams: RippleShader.FadeParams? 85 ) { 86 srcFadeParams?.let { 87 destFadeParams.fadeInStart = it.fadeInStart 88 destFadeParams.fadeInEnd = it.fadeInEnd 89 destFadeParams.fadeOutStart = it.fadeOutStart 90 destFadeParams.fadeOutEnd = it.fadeOutEnd 91 } 92 } 93 } 94