• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.systemui.car.displayarea;
18 
19 import android.animation.Animator;
20 import android.animation.ValueAnimator;
21 import android.content.Context;
22 import android.util.ArrayMap;
23 import android.view.SurfaceControl;
24 import android.window.WindowContainerToken;
25 
26 import androidx.annotation.VisibleForTesting;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * Controller class of display area animations (between different states).
33  */
34 public class CarDisplayAreaAnimationController {
35     private static final String TAG = "CarDisplayAreaAnimationController";
36     private static final float FRACTION_START = 0f;
37     private static final float FRACTION_END = 1f;
38 
39     public static final int TRANSITION_DIRECTION_NONE = 0;
40     public static final int TRANSITION_DIRECTION_TRIGGER = 1;
41     public static final int TRANSITION_DIRECTION_EXIT = 2;
42 
43     private final CarDisplayAreaTransactionHelper mSurfaceTransactionHelper;
44     private final ArrayMap<WindowContainerToken,
45             CarDisplayAreaTransitionAnimator>
46             mAnimatorMap = new ArrayMap<>();
47 
48     /**
49      * Constructor of CarDisplayAreaAnimationController
50      */
CarDisplayAreaAnimationController(Context context)51     public CarDisplayAreaAnimationController(Context context) {
52         mSurfaceTransactionHelper = new CarDisplayAreaTransactionHelper(context);
53     }
54 
55     @SuppressWarnings("unchecked")
getAnimator( WindowContainerToken token, SurfaceControl leash, float startPos, float endPos)56     CarDisplayAreaTransitionAnimator getAnimator(
57             WindowContainerToken token, SurfaceControl leash,
58             float startPos, float endPos) {
59         CarDisplayAreaTransitionAnimator animator = mAnimatorMap.get(token);
60         if (animator == null) {
61             mAnimatorMap.put(token, setupDisplayAreaTransitionAnimator(
62                     CarDisplayAreaTransitionAnimator.ofYOffset(
63                             token, leash, startPos, endPos)));
64         } else if (animator.isRunning()) {
65             animator.updateEndValue(endPos);
66         } else {
67             animator.cancel();
68             mAnimatorMap.put(token, setupDisplayAreaTransitionAnimator(
69                     CarDisplayAreaTransitionAnimator.ofYOffset(
70                             token, leash, startPos, endPos)));
71         }
72         return mAnimatorMap.get(token);
73     }
74 
75     ArrayMap<WindowContainerToken,
getAnimatorMap()76             CarDisplayAreaTransitionAnimator> getAnimatorMap() {
77         return mAnimatorMap;
78     }
79 
isAnimatorsConsumed()80     boolean isAnimatorsConsumed() {
81         return mAnimatorMap.isEmpty();
82     }
83 
removeAnimator(WindowContainerToken token)84     void removeAnimator(WindowContainerToken token) {
85         CarDisplayAreaTransitionAnimator animator = mAnimatorMap.remove(token);
86         if (animator != null && animator.isRunning()) {
87             animator.cancel();
88         }
89     }
90 
setupDisplayAreaTransitionAnimator( CarDisplayAreaTransitionAnimator animator)91     CarDisplayAreaTransitionAnimator setupDisplayAreaTransitionAnimator(
92             CarDisplayAreaTransitionAnimator animator) {
93         animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
94         animator.setInterpolator(new CarCubicBezierInterpolator(0.5f, 0, 0, 1));
95         animator.setFloatValues(FRACTION_START, FRACTION_END);
96         return animator;
97     }
98 
99     /**
100      * Animator for display area transition animation which supports both alpha and bounds
101      * animation.
102      */
103     public abstract static class CarDisplayAreaTransitionAnimator extends
104             ValueAnimator implements
105             ValueAnimator.AnimatorUpdateListener,
106             ValueAnimator.AnimatorListener {
107 
108         private final SurfaceControl mLeash;
109         private final WindowContainerToken mToken;
110         private float mStartValue;
111         private float mEndValue;
112         private float mCurrentValue;
113 
114         private final List<CarDisplayAreaAnimationCallback>
115                 mDisplayAreaAnimationCallbacks =
116                 new ArrayList<>();
117         private CarDisplayAreaTransactionHelper mSurfaceTransactionHelper;
118         private final CarDisplayAreaTransactionHelper.SurfaceControlTransactionFactory
119                 mSurfaceControlTransactionFactory;
120 
121         int mTransitionDirection;
122 
CarDisplayAreaTransitionAnimator(WindowContainerToken token, SurfaceControl leash, float startValue, float endValue)123         private CarDisplayAreaTransitionAnimator(WindowContainerToken token,
124                 SurfaceControl leash,
125                 float startValue, float endValue) {
126             mLeash = leash;
127             mToken = token;
128             mStartValue = startValue;
129             mEndValue = endValue;
130             addListener(this);
131             addUpdateListener(this);
132             mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
133             mTransitionDirection = TRANSITION_DIRECTION_NONE;
134         }
135 
136         @Override
onAnimationStart(Animator animation)137         public void onAnimationStart(Animator animation) {
138             mCurrentValue = mStartValue;
139             mDisplayAreaAnimationCallbacks.forEach(
140                     callback -> callback.onAnimationStart(this)
141             );
142         }
143 
144         @Override
onAnimationEnd(Animator animation)145         public void onAnimationEnd(Animator animation) {
146             mCurrentValue = mEndValue;
147             SurfaceControl.Transaction tx = newSurfaceControlTransaction();
148             onEndTransaction(mLeash, tx);
149             mDisplayAreaAnimationCallbacks.forEach(
150                     callback -> callback.onAnimationEnd(tx, this)
151             );
152             mDisplayAreaAnimationCallbacks.clear();
153         }
154 
155         @Override
onAnimationCancel(Animator animation)156         public void onAnimationCancel(Animator animation) {
157             mCurrentValue = mEndValue;
158             mDisplayAreaAnimationCallbacks.forEach(
159                     callback -> callback.onAnimationCancel(this)
160             );
161             mDisplayAreaAnimationCallbacks.clear();
162         }
163 
164         @Override
onAnimationRepeat(Animator animation)165         public void onAnimationRepeat(Animator animation) {
166         }
167 
168         @Override
onAnimationUpdate(ValueAnimator animation)169         public void onAnimationUpdate(ValueAnimator animation) {
170             SurfaceControl.Transaction tx = newSurfaceControlTransaction();
171             mDisplayAreaAnimationCallbacks.forEach(
172                     callback -> callback.onAnimationUpdate(0f, mCurrentValue)
173             );
174             applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
175         }
176 
onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx)177         void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
178         }
179 
onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx)180         void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
181         }
182 
applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction)183         abstract void applySurfaceControlTransaction(SurfaceControl leash,
184                 SurfaceControl.Transaction tx, float fraction);
185 
getSurfaceTransactionHelper()186         CarDisplayAreaTransactionHelper getSurfaceTransactionHelper() {
187             return mSurfaceTransactionHelper;
188         }
189 
setSurfaceTransactionHelper(CarDisplayAreaTransactionHelper helper)190         void setSurfaceTransactionHelper(CarDisplayAreaTransactionHelper helper) {
191             mSurfaceTransactionHelper = helper;
192         }
193 
addDisplayAreaAnimationCallback( CarDisplayAreaAnimationCallback callback)194         CarDisplayAreaTransitionAnimator addDisplayAreaAnimationCallback(
195                 CarDisplayAreaAnimationCallback callback) {
196             mDisplayAreaAnimationCallbacks.add(callback);
197             return this;
198         }
199 
getToken()200         WindowContainerToken getToken() {
201             return mToken;
202         }
203 
getStartValue()204         float getStartValue() {
205             return mStartValue;
206         }
207 
getEndValue()208         float getEndValue() {
209             return mEndValue;
210         }
211 
setCurrentValue(float value)212         void setCurrentValue(float value) {
213             mCurrentValue = value;
214         }
215 
216         /**
217          * Updates the {@link #mEndValue}.
218          */
updateEndValue(float endValue)219         void updateEndValue(float endValue) {
220             mEndValue = endValue;
221         }
222 
newSurfaceControlTransaction()223         SurfaceControl.Transaction newSurfaceControlTransaction() {
224             return mSurfaceControlTransactionFactory.getTransaction();
225         }
226 
227         @VisibleForTesting
ofYOffset( WindowContainerToken token, SurfaceControl leash, float startValue, float endValue)228         static CarDisplayAreaTransitionAnimator ofYOffset(
229                 WindowContainerToken token,
230                 SurfaceControl leash, float startValue, float endValue) {
231 
232             return new CarDisplayAreaTransitionAnimator(
233                     token, leash, startValue, endValue) {
234 
235                 private float getCastedFractionValue(float start, float end, float fraction) {
236                     return ((end - start) * fraction) + start;
237                 }
238 
239                 @Override
240                 void applySurfaceControlTransaction(SurfaceControl leash,
241                         SurfaceControl.Transaction tx, float fraction) {
242                     float start = getStartValue();
243                     float end = getEndValue();
244                     float currentValue = getCastedFractionValue(start, end, fraction);
245                     setCurrentValue(currentValue);
246                     getSurfaceTransactionHelper()
247                             .round(tx, leash)
248                             .translate(tx, leash, currentValue);
249                     tx.apply();
250                 }
251 
252                 @Override
253                 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
254                     getSurfaceTransactionHelper()
255                             .round(tx, leash)
256                             .translate(tx, leash, getStartValue());
257                     tx.apply();
258                 }
259             };
260         }
261     }
262 }
263 
264