blob: 7f7e1085d49730397f14547b3a973bbc853d3fed [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
Winson Chungc4d4ee82020-05-05 12:51:06 -070068 public static boolean isInPipDirection(@TransitionDirection int direction) {
69 return direction == TRANSITION_DIRECTION_TO_PIP;
70 }
71
Hongwei Wang5c52ff82020-04-20 16:02:30 -070072 public static boolean isOutPipDirection(@TransitionDirection int direction) {
73 return direction == TRANSITION_DIRECTION_TO_FULLSCREEN
74 || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
75 }
76
Hongwei Wang85cf41f2020-01-15 15:14:47 -080077 private final Interpolator mFastOutSlowInInterpolator;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070078 private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080079
80 private PipTransitionAnimator mCurrentAnimator;
81
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070082 @Inject
83 PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080084 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
85 com.android.internal.R.interpolator.fast_out_slow_in);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070086 mSurfaceTransactionHelper = helper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080087 }
88
Hongwei Wangdf8bb002020-03-03 17:41:02 -080089 @SuppressWarnings("unchecked")
90 PipTransitionAnimator getAnimator(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -080091 Rect destinationBounds, float alphaStart, float alphaEnd) {
92 if (mCurrentAnimator == null) {
93 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -080094 PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080095 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
96 && mCurrentAnimator.isRunning()) {
97 mCurrentAnimator.updateEndValue(alphaEnd);
98 } else {
99 mCurrentAnimator.cancel();
100 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800101 PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800102 }
103 return mCurrentAnimator;
104 }
105
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800106 @SuppressWarnings("unchecked")
107 PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800108 if (mCurrentAnimator == null) {
109 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800110 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Winson Chungc4d4ee82020-05-05 12:51:06 -0700111 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
112 && mCurrentAnimator.isRunning()) {
113 // If we are still animating the fade into pip, then just move the surface and ensure
114 // we update with the new destination bounds, but don't interrupt the existing animation
115 // with a new bounds
116 mCurrentAnimator.setDestinationBounds(endBounds);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800117 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
118 && mCurrentAnimator.isRunning()) {
119 mCurrentAnimator.setDestinationBounds(endBounds);
120 // construct new Rect instances in case they are recycled
121 mCurrentAnimator.updateEndValue(new Rect(endBounds));
122 } else {
123 mCurrentAnimator.cancel();
124 mCurrentAnimator = setupPipTransitionAnimator(
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800125 PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800126 }
127 return mCurrentAnimator;
128 }
129
Hongwei Wang951dc022020-03-30 16:16:16 -0700130 PipTransitionAnimator getCurrentAnimator() {
131 return mCurrentAnimator;
132 }
133
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800134 private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700135 animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800136 animator.setInterpolator(mFastOutSlowInInterpolator);
137 animator.setFloatValues(FRACTION_START, FRACTION_END);
138 return animator;
139 }
140
141 /**
142 * Additional callback interface for PiP animation
143 */
144 public static class PipAnimationCallback {
145 /**
146 * Called when PiP animation is started.
147 */
Winson Chung55701472020-03-04 19:30:30 -0800148 public void onPipAnimationStart(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800149
150 /**
151 * Called when PiP animation is ended.
152 */
Winson Chung55701472020-03-04 19:30:30 -0800153 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800154 PipTransitionAnimator animator) {}
155
156 /**
157 * Called when PiP animation is cancelled.
158 */
Winson Chung55701472020-03-04 19:30:30 -0800159 public void onPipAnimationCancel(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800160 }
161
162 /**
163 * Animator for PiP transition animation which supports both alpha and bounds animation.
164 * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
165 */
166 public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
167 ValueAnimator.AnimatorUpdateListener,
168 ValueAnimator.AnimatorListener {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800169 private final SurfaceControl mLeash;
170 private final @AnimationType int mAnimationType;
171 private final Rect mDestinationBounds = new Rect();
172
Tony Huang5ebe0192020-05-15 19:27:54 +0800173 protected T mCurrentValue;
174 protected T mStartValue;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800175 private T mEndValue;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800176 private PipAnimationCallback mPipAnimationCallback;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700177 private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
178 mSurfaceControlTransactionFactory;
179 private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800180 private @TransitionDirection int mTransitionDirection;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800181
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800182 private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
183 Rect destinationBounds, T startValue, T endValue) {
Winson Chung55701472020-03-04 19:30:30 -0800184 mLeash = leash;
185 mAnimationType = animationType;
186 mDestinationBounds.set(destinationBounds);
187 mStartValue = startValue;
188 mEndValue = endValue;
189 addListener(this);
190 addUpdateListener(this);
191 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800192 mTransitionDirection = TRANSITION_DIRECTION_NONE;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800193 }
194
195 @Override
196 public void onAnimationStart(Animator animation) {
197 mCurrentValue = mStartValue;
Hongwei Wang09c1f042020-03-18 13:47:45 -0700198 onStartTransaction(mLeash, newSurfaceControlTransaction());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800199 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800200 mPipAnimationCallback.onPipAnimationStart(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800201 }
202 }
203
204 @Override
205 public void onAnimationUpdate(ValueAnimator animation) {
206 applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
207 animation.getAnimatedFraction());
208 }
209
210 @Override
211 public void onAnimationEnd(Animator animation) {
212 mCurrentValue = mEndValue;
213 final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
Hongwei Wang09c1f042020-03-18 13:47:45 -0700214 onEndTransaction(mLeash, tx);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800215 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800216 mPipAnimationCallback.onPipAnimationEnd(tx, this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800217 }
218 }
219
220 @Override
221 public void onAnimationCancel(Animator animation) {
222 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800223 mPipAnimationCallback.onPipAnimationCancel(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800224 }
225 }
226
227 @Override public void onAnimationRepeat(Animator animation) {}
228
229 @AnimationType int getAnimationType() {
230 return mAnimationType;
231 }
232
233 PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
234 mPipAnimationCallback = callback;
235 return this;
236 }
237
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800238 @TransitionDirection int getTransitionDirection() {
239 return mTransitionDirection;
240 }
241
242 PipTransitionAnimator<T> setTransitionDirection(@TransitionDirection int direction) {
243 if (direction != TRANSITION_DIRECTION_SAME) {
244 mTransitionDirection = direction;
245 }
246 return this;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800247 }
248
249 T getStartValue() {
250 return mStartValue;
251 }
252
253 T getEndValue() {
254 return mEndValue;
255 }
256
257 Rect getDestinationBounds() {
258 return mDestinationBounds;
259 }
260
261 void setDestinationBounds(Rect destinationBounds) {
262 mDestinationBounds.set(destinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700263 if (mAnimationType == ANIM_TYPE_ALPHA) {
264 onStartTransaction(mLeash, newSurfaceControlTransaction());
265 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800266 }
267
268 void setCurrentValue(T value) {
269 mCurrentValue = value;
270 }
271
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800272 boolean shouldApplyCornerRadius() {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700273 return !isOutPipDirection(mTransitionDirection);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800274 }
275
Hongwei Wang09c1f042020-03-18 13:47:45 -0700276 boolean inScaleTransition() {
277 if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700278 return !isInPipDirection(getTransitionDirection());
Hongwei Wang09c1f042020-03-18 13:47:45 -0700279 }
280
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800281 /**
282 * Updates the {@link #mEndValue}.
283 *
284 * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
285 * This is typically used when we receive a shelf height adjustment during the bounds
286 * animation. In which case we can update the end bounds and keep the existing animation
287 * running instead of cancelling it.
288 */
289 void updateEndValue(T endValue) {
290 mEndValue = endValue;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800291 }
292
293 SurfaceControl.Transaction newSurfaceControlTransaction() {
294 return mSurfaceControlTransactionFactory.getTransaction();
295 }
296
297 @VisibleForTesting
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700298 void setSurfaceControlTransactionFactory(
299 PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800300 mSurfaceControlTransactionFactory = factory;
301 }
302
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700303 PipSurfaceTransactionHelper getSurfaceTransactionHelper() {
304 return mSurfaceTransactionHelper;
305 }
306
307 void setSurfaceTransactionHelper(PipSurfaceTransactionHelper helper) {
308 mSurfaceTransactionHelper = helper;
309 }
310
Hongwei Wang09c1f042020-03-18 13:47:45 -0700311 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
312
313 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
314
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800315 abstract void applySurfaceControlTransaction(SurfaceControl leash,
316 SurfaceControl.Transaction tx, float fraction);
317
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800318 static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800319 Rect destinationBounds, float startValue, float endValue) {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800320 return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800321 destinationBounds, startValue, endValue) {
322 @Override
323 void applySurfaceControlTransaction(SurfaceControl leash,
324 SurfaceControl.Transaction tx, float fraction) {
325 final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
326 setCurrentValue(alpha);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700327 getSurfaceTransactionHelper().alpha(tx, leash, alpha);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700328 tx.apply();
329 }
330
331 @Override
332 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
333 getSurfaceTransactionHelper()
334 .crop(tx, leash, getDestinationBounds())
335 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700336 tx.show(leash);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800337 tx.apply();
338 }
Tony Huang5ebe0192020-05-15 19:27:54 +0800339
340 @Override
341 void updateEndValue(Float endValue) {
342 super.updateEndValue(endValue);
343 mStartValue = mCurrentValue;
344 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800345 };
346 }
347
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800348 static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800349 Rect startValue, Rect endValue) {
350 // construct new Rect instances in case they are recycled
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800351 return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800352 endValue, new Rect(startValue), new Rect(endValue)) {
353 private final Rect mTmpRect = new Rect();
354
355 private int getCastedFractionValue(float start, float end, float fraction) {
356 return (int) (start * (1 - fraction) + end * fraction + .5f);
357 }
358
359 @Override
360 void applySurfaceControlTransaction(SurfaceControl leash,
361 SurfaceControl.Transaction tx, float fraction) {
362 final Rect start = getStartValue();
363 final Rect end = getEndValue();
364 mTmpRect.set(
365 getCastedFractionValue(start.left, end.left, fraction),
366 getCastedFractionValue(start.top, end.top, fraction),
367 getCastedFractionValue(start.right, end.right, fraction),
368 getCastedFractionValue(start.bottom, end.bottom, fraction));
369 setCurrentValue(mTmpRect);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700370 if (inScaleTransition()) {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700371 if (isOutPipDirection(getTransitionDirection())) {
372 getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect);
373 } else {
374 getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
375 }
Hongwei Wang09c1f042020-03-18 13:47:45 -0700376 } else {
377 getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800378 }
379 tx.apply();
380 }
Hongwei Wang09c1f042020-03-18 13:47:45 -0700381
382 @Override
383 void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
384 getSurfaceTransactionHelper()
385 .alpha(tx, leash, 1f)
386 .round(tx, leash, shouldApplyCornerRadius());
Winson Chung1b5d0552020-04-06 19:28:49 -0700387 tx.show(leash);
Hongwei Wang09c1f042020-03-18 13:47:45 -0700388 tx.apply();
389 }
390
391 @Override
392 void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
393 if (!inScaleTransition()) return;
394 // NOTE: intentionally does not apply the transaction here.
395 // this end transaction should get executed synchronously with the final
396 // WindowContainerTransaction in task organizer
Robert Carrb80a55c2020-05-05 15:17:51 -0700397 getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
398 .crop(tx, leash, getDestinationBounds());
Hongwei Wang09c1f042020-03-18 13:47:45 -0700399 }
Tony Huang5ebe0192020-05-15 19:27:54 +0800400
401 @Override
402 void updateEndValue(Rect endValue) {
403 super.updateEndValue(endValue);
404 if (mStartValue != null && mCurrentValue != null) {
405 mStartValue.set(mCurrentValue);
406 }
407 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800408 };
409 }
410 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800411}