1 /*
2  * Copyright (C) 2023 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.keyguard.ui.binder
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.animation.ValueAnimator
22 import android.graphics.Matrix
23 import android.util.Log
24 import android.view.RemoteAnimationTarget
25 import android.view.SurfaceControl
26 import android.view.SyncRtSurfaceTransactionApplier
27 import android.view.View
28 import androidx.dynamicanimation.animation.FloatValueHolder
29 import androidx.dynamicanimation.animation.SpringAnimation
30 import androidx.dynamicanimation.animation.SpringForce
31 import com.android.keyguard.KeyguardViewController
32 import com.android.systemui.dagger.SysUISingleton
33 import com.android.systemui.dagger.qualifiers.Main
34 import com.android.systemui.keyguard.TAG
35 import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
36 import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
37 import com.android.wm.shell.animation.Interpolators
38 import java.util.concurrent.Executor
39 import javax.inject.Inject
40 
41 /**
42  * Applies [KeyguardSurfaceBehindViewParams] to a RemoteAnimationTarget, starting and managing
43  * animations as needed.
44  */
45 @SysUISingleton
46 class KeyguardSurfaceBehindParamsApplier
47 @Inject
48 constructor(
49     @Main private val executor: Executor,
50     private val keyguardViewController: KeyguardViewController,
51     private val interactor: KeyguardSurfaceBehindInteractor,
52 ) {
53     private var surfaceBehind: RemoteAnimationTarget? = null
54     private val surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
55         get() = SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view)
56 
57     private val matrix = Matrix()
58     private val tmpFloat = FloatArray(9)
59 
60     private var animatedTranslationY = FloatValueHolder()
61     private val translateYSpring =
62         SpringAnimation(animatedTranslationY).apply {
63             spring =
64                 SpringForce().apply {
65                     stiffness = 200f
66                     dampingRatio = 1f
67                 }
68             addUpdateListener { _, _, _ -> applyToSurfaceBehind() }
69             addEndListener { _, _, _, _ ->
70                 try {
71                     updateIsAnimatingSurface()
72                 } catch (e: NullPointerException) {
73                     // TODO(b/291645410): Remove when we can isolate DynamicAnimations.
74                     e.printStackTrace()
75                 }
76             }
77         }
78 
79     private var animatedAlpha = 0f
80     private var alphaAnimator =
81         ValueAnimator.ofFloat(0f, 1f).apply {
82             duration = 500
83             interpolator = Interpolators.ALPHA_IN
84             addUpdateListener {
85                 animatedAlpha = it.animatedValue as Float
86                 applyToSurfaceBehind()
87             }
88             addListener(
89                 object : AnimatorListenerAdapter() {
90                     override fun onAnimationEnd(animation: Animator) {
91                         updateIsAnimatingSurface()
92                     }
93                 }
94             )
95         }
96 
97     /**
98      * ViewParams to apply to the surface provided to [applyParamsToSurface]. If the surface is null
99      * these will be applied once someone gives us a surface via [applyParamsToSurface].
100      */
101     var viewParams: KeyguardSurfaceBehindModel = KeyguardSurfaceBehindModel()
102         set(newParams) {
103             field = newParams
104             startOrUpdateAnimators()
105             applyToSurfaceBehind()
106         }
107 
108     /**
109      * Provides us with a surface to animate. We'll apply the [viewParams] to this surface and start
110      * any necessary animations.
111      */
112     fun applyParamsToSurface(surface: RemoteAnimationTarget) {
113         this.surfaceBehind = surface
114         startOrUpdateAnimators()
115     }
116 
117     /**
118      * Notifies us that the [RemoteAnimationTarget] has been released, one way or another.
119      * Attempting to animate a released target will cause a crash.
120      *
121      * This can be called either because we finished animating the surface naturally, or by WM
122      * because external factors cancelled the remote animation (timeout, re-lock, etc). If it's the
123      * latter, cancel any outstanding animations we have.
124      */
125     fun notifySurfaceReleased() {
126         surfaceBehind = null
127 
128         if (alphaAnimator.isRunning) {
129             alphaAnimator.cancel()
130         }
131 
132         if (translateYSpring.isRunning) {
133             translateYSpring.cancel()
134         }
135     }
136 
137     private fun startOrUpdateAnimators() {
138         if (surfaceBehind == null) {
139             return
140         }
141 
142         if (viewParams.willAnimateAlpha()) {
143             var fromAlpha = viewParams.animateFromAlpha
144 
145             if (alphaAnimator.isRunning) {
146                 alphaAnimator.cancel()
147                 fromAlpha = animatedAlpha
148             }
149 
150             alphaAnimator.setFloatValues(fromAlpha, viewParams.alpha)
151             alphaAnimator.start()
152         }
153 
154         if (viewParams.willAnimateTranslationY()) {
155             if (!translateYSpring.isRunning) {
156                 // If the spring isn't running yet, set the start value. Otherwise, respect the
157                 // current position.
158                 animatedTranslationY.value = viewParams.animateFromTranslationY
159             }
160 
161             translateYSpring.animateToFinalPosition(viewParams.translationY)
162         }
163 
164         updateIsAnimatingSurface()
165     }
166 
167     private fun updateIsAnimatingSurface() {
168         interactor.setAnimatingSurface(translateYSpring.isRunning || alphaAnimator.isRunning)
169     }
170 
171     private fun applyToSurfaceBehind() {
172         surfaceBehind?.leash?.let { sc ->
173             executor.execute {
174                 if (surfaceBehind == null) {
175                     Log.d(
176                         TAG,
177                         "Attempting to modify params of surface that isn't " +
178                             "animating. Ignoring."
179                     )
180                     matrix.set(Matrix.IDENTITY_MATRIX)
181                     return@execute
182                 }
183 
184                 val translationY =
185                     if (translateYSpring.isRunning) animatedTranslationY.value
186                     else viewParams.translationY
187 
188                 val alpha =
189                     if (alphaAnimator.isRunning) {
190                         animatedAlpha
191                     } else {
192                         viewParams.alpha
193                     }
194 
195                 if (
196                     keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
197                         sc.isValid
198                 ) {
199                     with(SurfaceControl.Transaction()) {
200                         setMatrix(
201                             sc,
202                             matrix.apply { setTranslate(/* dx= */ 0f, translationY) },
203                             tmpFloat
204                         )
205                         setAlpha(sc, alpha)
206                         apply()
207                     }
208                 } else {
209                     surfaceTransactionApplier.scheduleApply(
210                         SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(sc)
211                             .withMatrix(matrix.apply { setTranslate(/* dx= */ 0f, translationY) })
212                             .withAlpha(alpha)
213                             .build()
214                     )
215                 }
216             }
217         }
218     }
219 }
220