blob: f72de11a01ed2a63359e02bc1021771bafcba434 [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 Wang5c52ff82020-04-20 16:02:30 -070056 public static final int TRANSITION_DIRECTION_TO_SPLIT_SCREEN = 4;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080057
58 @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
59 TRANSITION_DIRECTION_NONE,
60 TRANSITION_DIRECTION_SAME,
61 TRANSITION_DIRECTION_TO_PIP,
Hongwei Wang5c52ff82020-04-20 16:02:30 -070062 TRANSITION_DIRECTION_TO_FULLSCREEN,
63 TRANSITION_DIRECTION_TO_SPLIT_SCREEN
Hongwei Wangdf8bb002020-03-03 17:41:02 -080064 })
65 @Retention(RetentionPolicy.SOURCE)
Hongwei Wang221fe3d2020-03-26 13:13:04 -070066 public @interface TransitionDirection {}
Hongwei Wangdf8bb002020-03-03 17:41:02 -080067
Hongwei Wang5c52ff82020-04-20 16:02:30 -070068 public static boolean isOutPipDirection(@TransitionDirection int direction) {
69 return direction == TRANSITION_DIRECTION_TO_FULLSCREEN
70 || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
71 }
72
Hongwei Wang85cf41f2020-01-15 15:14:47 -080073 private final Interpolator mFastOutSlowInInterpolator;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070074 private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080075
76 private PipTransitionAnimator mCurrentAnimator;
77
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070078 @Inject
79 PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080080 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
81 com.android.internal.R.interpolator.fast_out_slow_in);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070082 mSurfaceTransactionHelper = helper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080083 }
84
Hongwei Wangdf8bb002020-03-03 17:41:02 -080085 @SuppressWarnings("unchecked")
86 PipTransitionAnimator getAnimator(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -080087 Rect destinationBounds, float alphaStart, float alphaEnd) {
88 if (mCurrentAnimator == null) {
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 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
92 && mCurrentAnimator.isRunning()) {
93 mCurrentAnimator.updateEndValue(alphaEnd);
94 } else {
95 mCurrentAnimator.cancel();
96 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -080097 PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080098 }
99 return mCurrentAnimator;
100 }
101
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800102 @SuppressWarnings("unchecked")
103 PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800104 if (mCurrentAnimator == null) {
105 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800106 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800107 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
108 && mCurrentAnimator.isRunning()) {
109 mCurrentAnimator.setDestinationBounds(endBounds);
110 // construct new Rect instances in case they are recycled
111 mCurrentAnimator.updateEndValue(new Rect(endBounds));
112 } else {
113 mCurrentAnimator.cancel();
114 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800115 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800116 }
117 return mCurrentAnimator;
118 }
119
Hongwei Wang951dc022020-03-30 16:16:16 -0700120 PipTransitionAnimator getCurrentAnimator() {
121 return mCurrentAnimator;
122 }
123
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800124 private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700125 animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800126 animator.setInterpolator(mFastOutSlowInInterpolator);
127 animator.setFloatValues(FRACTION_START, FRACTION_END);
128 return animator;
129 }
130
131 /**
132 * Additional callback interface for PiP animation
133 */
134 public static class PipAnimationCallback {
135 /**
136 * Called when PiP animation is started.
137 */
Winson Chung55701472020-03-04 19:30:30 -0800138 public void onPipAnimationStart(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800139
140 /**
141 * Called when PiP animation is ended.
142 */
Winson Chung55701472020-03-04 19:30:30 -0800143 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800144 PipTransitionAnimator animator) {}
145
146 /**
147 * Called when PiP animation is cancelled.
148 */
Winson Chung55701472020-03-04 19:30:30 -0800149 public void onPipAnimationCancel(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800150 }
151
152 /**
153 * Animator for PiP transition animation which supports both alpha and bounds animation.
154 * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
155 */
156 public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
157 ValueAnimator.AnimatorUpdateListener,
158 ValueAnimator.AnimatorListener {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800159 private final SurfaceControl mLeash;
160 private final @AnimationType int mAnimationType;
161 private final Rect mDestinationBounds = new Rect();
162
163 private T mStartValue;
164 private T mEndValue;
165 private T mCurrentValue;
166 private PipAnimationCallback mPipAnimationCallback;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700167 private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
168 mSurfaceControlTransactionFactory;
169 private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800170 private @TransitionDirection int mTransitionDirection;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800171
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800172 private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
173 Rect destinationBounds, T startValue, T endValue) {
Winson Chung55701472020-03-04 19:30:30 -0800174 mLeash = leash;
175 mAnimationType = animationType;
176 mDestinationBounds.set(destinationBounds);
177 mStartValue = startValue;
178 mEndValue = endValue;
179 addListener(this);
180 addUpdateListener(this);
181 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800182 mTransitionDirection = TRANSITION_DIRECTION_NONE;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800183 }
184
185 @Override
186 public void onAnimationStart(Animator animation) {
187 mCurrentValue = mStartValue;
Hongwei Wang09c1f042020-03-18 13:47:45 -0700188 onStartTransaction(mLeash, newSurfaceControlTransaction());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800189 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800190 mPipAnimationCallback.onPipAnimationStart(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800191 }
192 }
193
194 @Override
195 public void onAnimationUpdate(ValueAnimator animation) {
196 applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
197 animation.getAnimatedFraction());
198 }
199
200 @Override
201 public void onAnimationEnd(Animator animation) {
202 mCurrentValue = mEndValue;
203 final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
Hongwei Wang09c1f042020-03-18 13:47:45 -0700204 onEndTransaction(mLeash, tx);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800205 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800206 mPipAnimationCallback.onPipAnimationEnd(tx, this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800207 }
208 }
209
210 @Override
211 public void onAnimationCancel(Animator animation) {
212 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800213 mPipAnimationCallback.onPipAnimationCancel(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800214 }
215 }
216
217 @Override public void onAnimationRepeat(Animator animation) {}
218
219 @AnimationType int getAnimationType() {
220 return mAnimationType;
221 }
222
223 PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
224 mPipAnimationCallback = callback;
225 return this;
226 }
227
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800228 @TransitionDirection int getTransitionDirection() {
229 return mTransitionDirection;
230 }
231
232 PipTransitionAnimator<T> setTransitionDirection(@TransitionDirection int direction) {
233 if (direction != TRANSITION_DIRECTION_SAME) {
234 mTransitionDirection = direction;
235 }
236 return this;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800237 }
238
239 T getStartValue() {
240 return mStartValue;
241 }
242
243 T getEndValue() {
244 return mEndValue;
245 }
246
247 Rect getDestinationBounds() {
248 return mDestinationBounds;
249 }
250
251 void setDestinationBounds(Rect destinationBounds) {
252 mDestinationBounds.set(destinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700253 if (mAnimationType == ANIM_TYPE_ALPHA) {
254 onStartTransaction(mLeash, newSurfaceControlTransaction());
255 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800256 }
257
258 void setCurrentValue(T value) {
259 mCurrentValue = value;
260 }
261
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800262 boolean shouldApplyCornerRadius() {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700263 return !isOutPipDirection(mTransitionDirection);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800264 }
265
Hongwei Wang09c1f042020-03-18 13:47:45 -0700266 boolean inScaleTransition() {
267 if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
268 final int direction = getTransitionDirection();
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700269 return !isOutPipDirection(direction) && direction != TRANSITION_DIRECTION_TO_PIP;
Hongwei Wang09c1f042020-03-18 13:47:45 -0700270 }
271
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800272 /**
273 * Updates the {@link #mEndValue}.
274 *
275 * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
276 * This is typically used when we receive a shelf height adjustment during the bounds
277 * animation. In which case we can update the end bounds and keep the existing animation
278 * running instead of cancelling it.
279 */
280 void updateEndValue(T endValue) {
281 mEndValue = endValue;
282 mStartValue = mCurrentValue;
283 }
284
285 SurfaceControl.Transaction newSurfaceControlTransaction() {
286 return mSurfaceControlTransactionFactory.getTransaction();
287 }
288
289 @VisibleForTesting
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700290 void setSurfaceControlTransactionFactory(
291 PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800292 mSurfaceControlTransactionFactory = factory;
293 }
294
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700295 PipSurfaceTransactionHelper getSurfaceTransactionHelper() {
296 return mSurfaceTransactionHelper;
297 }
298
299 void setSurfaceTransactionHelper(PipSurfaceTransactionHelper helper) {
300 mSurfaceTransactionHelper = helper;
301 }
302
Hongwei Wang09c1f042020-03-18 13:47:45 -0700303 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
304
305 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
306
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800307 abstract void applySurfaceControlTransaction(SurfaceControl leash,
308 SurfaceControl.Transaction tx, float fraction);
309
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800310 static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800311 Rect destinationBounds, float startValue, float endValue) {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800312 return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800313 destinationBounds, startValue, endValue) {
314 @Override
315 void applySurfaceControlTransaction(SurfaceControl leash,
316 SurfaceControl.Transaction tx, float fraction) {
317 final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
318 setCurrentValue(alpha);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700319 getSurfaceTransactionHelper().alpha(tx, leash, alpha);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700320 tx.apply();
321 }
322
323 @Override
324 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
325 getSurfaceTransactionHelper()
326 .crop(tx, leash, getDestinationBounds())
327 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700328 tx.show(leash);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800329 tx.apply();
330 }
331 };
332 }
333
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800334 static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800335 Rect startValue, Rect endValue) {
336 // construct new Rect instances in case they are recycled
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800337 return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800338 endValue, new Rect(startValue), new Rect(endValue)) {
339 private final Rect mTmpRect = new Rect();
340
341 private int getCastedFractionValue(float start, float end, float fraction) {
342 return (int) (start * (1 - fraction) + end * fraction + .5f);
343 }
344
345 @Override
346 void applySurfaceControlTransaction(SurfaceControl leash,
347 SurfaceControl.Transaction tx, float fraction) {
348 final Rect start = getStartValue();
349 final Rect end = getEndValue();
350 mTmpRect.set(
351 getCastedFractionValue(start.left, end.left, fraction),
352 getCastedFractionValue(start.top, end.top, fraction),
353 getCastedFractionValue(start.right, end.right, fraction),
354 getCastedFractionValue(start.bottom, end.bottom, fraction));
355 setCurrentValue(mTmpRect);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700356 if (inScaleTransition()) {
357 getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
358 } else {
359 getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800360 }
361 tx.apply();
362 }
Hongwei Wang09c1f042020-03-18 13:47:45 -0700363
364 @Override
365 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
366 getSurfaceTransactionHelper()
367 .alpha(tx, leash, 1f)
368 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700369 tx.show(leash);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700370 tx.apply();
371 }
372
373 @Override
374 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
375 if (!inScaleTransition()) return;
376 // NOTE: intentionally does not apply the transaction here.
377 // this end transaction should get executed synchronously with the final
378 // WindowContainerTransaction in task organizer
Robert Carrb80a55c2020-05-05 15:17:51 -0700379 getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
380 .crop(tx, leash, getDestinationBounds());
Hongwei Wang09c1f042020-03-18 13:47:45 -0700381 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800382 };
383 }
384 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800385}