1 /* 2 * 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 package com.android.systemui.surfaceeffects.turbulencenoise 17 18 import android.view.View 19 import androidx.annotation.VisibleForTesting 20 import java.util.Random 21 22 /** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */ 23 class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) { 24 25 companion object { 26 /** 27 * States of the turbulence noise animation. 28 * 29 * <p>The state is designed to be follow the order below: [AnimationState.EASE_IN], 30 * [AnimationState.MAIN], [AnimationState.EASE_OUT]. 31 */ 32 enum class AnimationState { 33 EASE_IN, 34 MAIN, 35 EASE_OUT, 36 NOT_PLAYING 37 } 38 } 39 40 private val random = Random() 41 42 /** Current state of the animation. */ 43 @VisibleForTesting 44 var state: AnimationState = AnimationState.NOT_PLAYING 45 set(value) { 46 field = value 47 if (state == AnimationState.NOT_PLAYING) { 48 turbulenceNoiseView.visibility = View.INVISIBLE 49 turbulenceNoiseView.clearConfig() 50 } else { 51 turbulenceNoiseView.visibility = View.VISIBLE 52 } 53 } 54 55 init { 56 turbulenceNoiseView.visibility = View.INVISIBLE 57 } 58 59 /** Updates the color of the noise. */ 60 fun updateNoiseColor(color: Int) { 61 if (state == AnimationState.NOT_PLAYING) { 62 return 63 } 64 turbulenceNoiseView.updateColor(color) 65 } 66 67 /** 68 * Plays [TurbulenceNoiseView] with the given config. 69 * 70 * <p>It plays ease-in, main, and ease-out animations in sequence. 71 */ 72 fun play(config: TurbulenceNoiseAnimationConfig) { 73 if (state != AnimationState.NOT_PLAYING) { 74 return // Ignore if any of the animation is playing. 75 } 76 77 turbulenceNoiseView.applyConfig(config) 78 playEaseInAnimation() 79 } 80 81 // TODO(b/237282226): Support force finish. 82 /** Finishes the main animation, which triggers the ease-out animation. */ 83 fun finish() { 84 if (state == AnimationState.MAIN) { 85 turbulenceNoiseView.finish(nextAnimation = this::playEaseOutAnimation) 86 } 87 } 88 89 private fun playEaseInAnimation() { 90 if (state != AnimationState.NOT_PLAYING) { 91 return 92 } 93 state = AnimationState.EASE_IN 94 95 // Add offset to avoid repetitive noise. 96 turbulenceNoiseView.playEaseIn( 97 offsetX = random.nextFloat(), 98 offsetY = random.nextFloat(), 99 this::playMainAnimation 100 ) 101 } 102 103 private fun playMainAnimation() { 104 if (state != AnimationState.EASE_IN) { 105 return 106 } 107 state = AnimationState.MAIN 108 109 turbulenceNoiseView.play(this::playEaseOutAnimation) 110 } 111 112 private fun playEaseOutAnimation() { 113 if (state != AnimationState.MAIN) { 114 return 115 } 116 state = AnimationState.EASE_OUT 117 118 turbulenceNoiseView.playEaseOut(onAnimationEnd = { state = AnimationState.NOT_PLAYING }) 119 } 120 } 121