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