blob: dba43430b490cf92ce82f514872fe2908c88cc02 [file] [log] [blame]
Hongwei Wang85cf41f2020-01-15 15:14:47 -08001/*
2 * Copyright (C) 2020 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
17package com.android.systemui.pip;
18
19import android.animation.Animator;
20import android.animation.ValueAnimator;
21import android.annotation.IntDef;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080022import android.content.Context;
23import android.graphics.Rect;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080024import android.view.SurfaceControl;
25import android.view.animation.AnimationUtils;
26import android.view.animation.Interpolator;
27
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.lang.annotation.Retention;
31import java.lang.annotation.RetentionPolicy;
32
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070033import javax.inject.Inject;
34
Hongwei Wang85cf41f2020-01-15 15:14:47 -080035/**
36 * Controller class of PiP animations (both from and to PiP mode).
37 */
38public class PipAnimationController {
39 private static final float FRACTION_START = 0f;
40 private static final float FRACTION_END = 1f;
41
Hongwei Wang85cf41f2020-01-15 15:14:47 -080042 public static final int ANIM_TYPE_BOUNDS = 0;
43 public static final int ANIM_TYPE_ALPHA = 1;
44
45 @IntDef(prefix = { "ANIM_TYPE_" }, value = {
46 ANIM_TYPE_BOUNDS,
47 ANIM_TYPE_ALPHA
48 })
49 @Retention(RetentionPolicy.SOURCE)
50 public @interface AnimationType {}
51
Hongwei Wang221fe3d2020-03-26 13:13:04 -070052 public static final int TRANSITION_DIRECTION_NONE = 0;
53 public static final int TRANSITION_DIRECTION_SAME = 1;
54 public static final int TRANSITION_DIRECTION_TO_PIP = 2;
55 public static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080056
57 @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
58 TRANSITION_DIRECTION_NONE,
59 TRANSITION_DIRECTION_SAME,
60 TRANSITION_DIRECTION_TO_PIP,
61 TRANSITION_DIRECTION_TO_FULLSCREEN
62 })
63 @Retention(RetentionPolicy.SOURCE)
Hongwei Wang221fe3d2020-03-26 13:13:04 -070064 public @interface TransitionDirection {}
Hongwei Wangdf8bb002020-03-03 17:41:02 -080065
Hongwei Wang85cf41f2020-01-15 15:14:47 -080066 private final Interpolator mFastOutSlowInInterpolator;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070067 private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080068
69 private PipTransitionAnimator mCurrentAnimator;
70
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070071 @Inject
72 PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080073 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
74 com.android.internal.R.interpolator.fast_out_slow_in);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070075 mSurfaceTransactionHelper = helper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080076 }
77
Hongwei Wangdf8bb002020-03-03 17:41:02 -080078 @SuppressWarnings("unchecked")
79 PipTransitionAnimator getAnimator(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -080080 Rect destinationBounds, float alphaStart, float alphaEnd) {
81 if (mCurrentAnimator == null) {
82 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -080083 PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080084 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
85 && mCurrentAnimator.isRunning()) {
86 mCurrentAnimator.updateEndValue(alphaEnd);
87 } else {
88 mCurrentAnimator.cancel();
89 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -080090 PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080091 }
92 return mCurrentAnimator;
93 }
94
Hongwei Wangdf8bb002020-03-03 17:41:02 -080095 @SuppressWarnings("unchecked")
96 PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080097 if (mCurrentAnimator == null) {
98 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -080099 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800100 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
101 && mCurrentAnimator.isRunning()) {
102 mCurrentAnimator.setDestinationBounds(endBounds);
103 // construct new Rect instances in case they are recycled
104 mCurrentAnimator.updateEndValue(new Rect(endBounds));
105 } else {
106 mCurrentAnimator.cancel();
107 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800108 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800109 }
110 return mCurrentAnimator;
111 }
112
Hongwei Wang951dc022020-03-30 16:16:16 -0700113 PipTransitionAnimator getCurrentAnimator() {
114 return mCurrentAnimator;
115 }
116
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800117 private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700118 animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800119 animator.setInterpolator(mFastOutSlowInInterpolator);
120 animator.setFloatValues(FRACTION_START, FRACTION_END);
121 return animator;
122 }
123
124 /**
125 * Additional callback interface for PiP animation
126 */
127 public static class PipAnimationCallback {
128 /**
129 * Called when PiP animation is started.
130 */
Winson Chung55701472020-03-04 19:30:30 -0800131 public void onPipAnimationStart(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800132
133 /**
134 * Called when PiP animation is ended.
135 */
Winson Chung55701472020-03-04 19:30:30 -0800136 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800137 PipTransitionAnimator animator) {}
138
139 /**
140 * Called when PiP animation is cancelled.
141 */
Winson Chung55701472020-03-04 19:30:30 -0800142 public void onPipAnimationCancel(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800143 }
144
145 /**
146 * Animator for PiP transition animation which supports both alpha and bounds animation.
147 * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
148 */
149 public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
150 ValueAnimator.AnimatorUpdateListener,
151 ValueAnimator.AnimatorListener {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800152 private final SurfaceControl mLeash;
153 private final @AnimationType int mAnimationType;
154 private final Rect mDestinationBounds = new Rect();
155
156 private T mStartValue;
157 private T mEndValue;
158 private T mCurrentValue;
159 private PipAnimationCallback mPipAnimationCallback;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700160 private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
161 mSurfaceControlTransactionFactory;
162 private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800163 private @TransitionDirection int mTransitionDirection;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800164
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800165 private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
166 Rect destinationBounds, T startValue, T endValue) {
Winson Chung55701472020-03-04 19:30:30 -0800167 mLeash = leash;
168 mAnimationType = animationType;
169 mDestinationBounds.set(destinationBounds);
170 mStartValue = startValue;
171 mEndValue = endValue;
172 addListener(this);
173 addUpdateListener(this);
174 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800175 mTransitionDirection = TRANSITION_DIRECTION_NONE;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800176 }
177
178 @Override
179 public void onAnimationStart(Animator animation) {
180 mCurrentValue = mStartValue;
Hongwei Wang09c1f042020-03-18 13:47:45 -0700181 onStartTransaction(mLeash, newSurfaceControlTransaction());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800182 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800183 mPipAnimationCallback.onPipAnimationStart(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800184 }
185 }
186
187 @Override
188 public void onAnimationUpdate(ValueAnimator animation) {
189 applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
190 animation.getAnimatedFraction());
191 }
192
193 @Override
194 public void onAnimationEnd(Animator animation) {
195 mCurrentValue = mEndValue;
196 final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
Hongwei Wang09c1f042020-03-18 13:47:45 -0700197 onEndTransaction(mLeash, tx);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800198 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800199 mPipAnimationCallback.onPipAnimationEnd(tx, this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800200 }
201 }
202
203 @Override
204 public void onAnimationCancel(Animator animation) {
205 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800206 mPipAnimationCallback.onPipAnimationCancel(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800207 }
208 }
209
210 @Override public void onAnimationRepeat(Animator animation) {}
211
212 @AnimationType int getAnimationType() {
213 return mAnimationType;
214 }
215
216 PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
217 mPipAnimationCallback = callback;
218 return this;
219 }
220
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800221 @TransitionDirection int getTransitionDirection() {
222 return mTransitionDirection;
223 }
224
225 PipTransitionAnimator<T> setTransitionDirection(@TransitionDirection int direction) {
226 if (direction != TRANSITION_DIRECTION_SAME) {
227 mTransitionDirection = direction;
228 }
229 return this;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800230 }
231
232 T getStartValue() {
233 return mStartValue;
234 }
235
236 T getEndValue() {
237 return mEndValue;
238 }
239
240 Rect getDestinationBounds() {
241 return mDestinationBounds;
242 }
243
244 void setDestinationBounds(Rect destinationBounds) {
245 mDestinationBounds.set(destinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700246 if (mAnimationType == ANIM_TYPE_ALPHA) {
247 onStartTransaction(mLeash, newSurfaceControlTransaction());
248 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800249 }
250
251 void setCurrentValue(T value) {
252 mCurrentValue = value;
253 }
254
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800255 boolean shouldApplyCornerRadius() {
256 return mTransitionDirection != TRANSITION_DIRECTION_TO_FULLSCREEN;
257 }
258
Hongwei Wang09c1f042020-03-18 13:47:45 -0700259 boolean inScaleTransition() {
260 if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
261 final int direction = getTransitionDirection();
262 return direction != TRANSITION_DIRECTION_TO_FULLSCREEN
263 && direction != TRANSITION_DIRECTION_TO_PIP;
264 }
265
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800266 /**
267 * Updates the {@link #mEndValue}.
268 *
269 * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
270 * This is typically used when we receive a shelf height adjustment during the bounds
271 * animation. In which case we can update the end bounds and keep the existing animation
272 * running instead of cancelling it.
273 */
274 void updateEndValue(T endValue) {
275 mEndValue = endValue;
276 mStartValue = mCurrentValue;
277 }
278
279 SurfaceControl.Transaction newSurfaceControlTransaction() {
280 return mSurfaceControlTransactionFactory.getTransaction();
281 }
282
283 @VisibleForTesting
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700284 void setSurfaceControlTransactionFactory(
285 PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800286 mSurfaceControlTransactionFactory = factory;
287 }
288
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700289 PipSurfaceTransactionHelper getSurfaceTransactionHelper() {
290 return mSurfaceTransactionHelper;
291 }
292
293 void setSurfaceTransactionHelper(PipSurfaceTransactionHelper helper) {
294 mSurfaceTransactionHelper = helper;
295 }
296
Hongwei Wang09c1f042020-03-18 13:47:45 -0700297 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
298
299 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
300
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800301 abstract void applySurfaceControlTransaction(SurfaceControl leash,
302 SurfaceControl.Transaction tx, float fraction);
303
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800304 static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800305 Rect destinationBounds, float startValue, float endValue) {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800306 return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800307 destinationBounds, startValue, endValue) {
308 @Override
309 void applySurfaceControlTransaction(SurfaceControl leash,
310 SurfaceControl.Transaction tx, float fraction) {
311 final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
312 setCurrentValue(alpha);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700313 getSurfaceTransactionHelper().alpha(tx, leash, alpha);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700314 tx.apply();
315 }
316
317 @Override
318 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
319 getSurfaceTransactionHelper()
320 .crop(tx, leash, getDestinationBounds())
321 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700322 tx.show(leash);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800323 tx.apply();
324 }
325 };
326 }
327
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800328 static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800329 Rect startValue, Rect endValue) {
330 // construct new Rect instances in case they are recycled
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800331 return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800332 endValue, new Rect(startValue), new Rect(endValue)) {
333 private final Rect mTmpRect = new Rect();
334
335 private int getCastedFractionValue(float start, float end, float fraction) {
336 return (int) (start * (1 - fraction) + end * fraction + .5f);
337 }
338
339 @Override
340 void applySurfaceControlTransaction(SurfaceControl leash,
341 SurfaceControl.Transaction tx, float fraction) {
342 final Rect start = getStartValue();
343 final Rect end = getEndValue();
344 mTmpRect.set(
345 getCastedFractionValue(start.left, end.left, fraction),
346 getCastedFractionValue(start.top, end.top, fraction),
347 getCastedFractionValue(start.right, end.right, fraction),
348 getCastedFractionValue(start.bottom, end.bottom, fraction));
349 setCurrentValue(mTmpRect);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700350 if (inScaleTransition()) {
351 getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
352 } else {
353 getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800354 }
355 tx.apply();
356 }
Hongwei Wang09c1f042020-03-18 13:47:45 -0700357
358 @Override
359 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
360 getSurfaceTransactionHelper()
361 .alpha(tx, leash, 1f)
362 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700363 tx.show(leash);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700364 tx.apply();
365 }
366
367 @Override
368 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
369 if (!inScaleTransition()) return;
370 // NOTE: intentionally does not apply the transaction here.
371 // this end transaction should get executed synchronously with the final
372 // WindowContainerTransaction in task organizer
373 getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds());
374 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800375 };
376 }
377 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800378}