blob: 36b5fade1929d52d245bcb00f0f07719648fec8e [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
33/**
34 * Controller class of PiP animations (both from and to PiP mode).
35 */
36public class PipAnimationController {
37 private static final float FRACTION_START = 0f;
38 private static final float FRACTION_END = 1f;
39
40 public static final int DURATION_NONE = 0;
41 public static final int DURATION_DEFAULT_MS = 425;
42 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
52 private final Interpolator mFastOutSlowInInterpolator;
53
54 private PipTransitionAnimator mCurrentAnimator;
55
56 PipAnimationController(Context context) {
57 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
58 com.android.internal.R.interpolator.fast_out_slow_in);
59 }
60
Winson Chung55701472020-03-04 19:30:30 -080061 PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
Hongwei Wang85cf41f2020-01-15 15:14:47 -080062 Rect destinationBounds, float alphaStart, float alphaEnd) {
63 if (mCurrentAnimator == null) {
64 mCurrentAnimator = setupPipTransitionAnimator(
Winson Chung55701472020-03-04 19:30:30 -080065 PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
66 alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080067 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
68 && mCurrentAnimator.isRunning()) {
69 mCurrentAnimator.updateEndValue(alphaEnd);
70 } else {
71 mCurrentAnimator.cancel();
72 mCurrentAnimator = setupPipTransitionAnimator(
Winson Chung55701472020-03-04 19:30:30 -080073 PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
74 alphaStart, alphaEnd));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080075 }
76 return mCurrentAnimator;
77 }
78
Winson Chung55701472020-03-04 19:30:30 -080079 PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
Hongwei Wang85cf41f2020-01-15 15:14:47 -080080 Rect startBounds, Rect endBounds) {
81 if (mCurrentAnimator == null) {
82 mCurrentAnimator = setupPipTransitionAnimator(
Winson Chung55701472020-03-04 19:30:30 -080083 PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
84 startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080085 } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
86 && mCurrentAnimator.isRunning()) {
87 mCurrentAnimator.setDestinationBounds(endBounds);
88 // construct new Rect instances in case they are recycled
89 mCurrentAnimator.updateEndValue(new Rect(endBounds));
90 } else {
91 mCurrentAnimator.cancel();
92 mCurrentAnimator = setupPipTransitionAnimator(
Winson Chung55701472020-03-04 19:30:30 -080093 PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
94 startBounds, endBounds));
Hongwei Wang85cf41f2020-01-15 15:14:47 -080095 }
96 return mCurrentAnimator;
97 }
98
Hongwei Wangea2ae852020-02-27 13:47:52 -080099 PipTransitionAnimator getCurrentAnimator() {
100 return mCurrentAnimator;
101 }
102
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800103 private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
104 animator.setInterpolator(mFastOutSlowInInterpolator);
105 animator.setFloatValues(FRACTION_START, FRACTION_END);
106 return animator;
107 }
108
109 /**
110 * Additional callback interface for PiP animation
111 */
112 public static class PipAnimationCallback {
113 /**
114 * Called when PiP animation is started.
115 */
Winson Chung55701472020-03-04 19:30:30 -0800116 public void onPipAnimationStart(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800117
118 /**
119 * Called when PiP animation is ended.
120 */
Winson Chung55701472020-03-04 19:30:30 -0800121 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800122 PipTransitionAnimator animator) {}
123
124 /**
125 * Called when PiP animation is cancelled.
126 */
Winson Chung55701472020-03-04 19:30:30 -0800127 public void onPipAnimationCancel(PipTransitionAnimator animator) {}
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800128 }
129
130 /**
131 * Animator for PiP transition animation which supports both alpha and bounds animation.
132 * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
133 */
134 public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
135 ValueAnimator.AnimatorUpdateListener,
136 ValueAnimator.AnimatorListener {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800137 private final boolean mScheduleFinishPip;
138 private final SurfaceControl mLeash;
139 private final @AnimationType int mAnimationType;
140 private final Rect mDestinationBounds = new Rect();
141
142 private T mStartValue;
143 private T mEndValue;
144 private T mCurrentValue;
145 private PipAnimationCallback mPipAnimationCallback;
146 private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
147
Winson Chung55701472020-03-04 19:30:30 -0800148 private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800149 @AnimationType int animationType, Rect destinationBounds,
150 T startValue, T endValue) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800151 mScheduleFinishPip = scheduleFinishPip;
Winson Chung55701472020-03-04 19:30:30 -0800152 mLeash = leash;
153 mAnimationType = animationType;
154 mDestinationBounds.set(destinationBounds);
155 mStartValue = startValue;
156 mEndValue = endValue;
157 addListener(this);
158 addUpdateListener(this);
159 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800160 }
161
162 @Override
163 public void onAnimationStart(Animator animation) {
164 mCurrentValue = mStartValue;
165 applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
166 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800167 mPipAnimationCallback.onPipAnimationStart(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800168 }
169 }
170
171 @Override
172 public void onAnimationUpdate(ValueAnimator animation) {
173 applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
174 animation.getAnimatedFraction());
175 }
176
177 @Override
178 public void onAnimationEnd(Animator animation) {
179 mCurrentValue = mEndValue;
180 final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
181 applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
182 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800183 mPipAnimationCallback.onPipAnimationEnd(tx, this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800184 }
185 }
186
187 @Override
188 public void onAnimationCancel(Animator animation) {
189 if (mPipAnimationCallback != null) {
Winson Chung55701472020-03-04 19:30:30 -0800190 mPipAnimationCallback.onPipAnimationCancel(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800191 }
192 }
193
194 @Override public void onAnimationRepeat(Animator animation) {}
195
196 @AnimationType int getAnimationType() {
197 return mAnimationType;
198 }
199
200 PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
201 mPipAnimationCallback = callback;
202 return this;
203 }
204
205 boolean shouldScheduleFinishPip() {
206 return mScheduleFinishPip;
207 }
208
209 T getStartValue() {
210 return mStartValue;
211 }
212
213 T getEndValue() {
214 return mEndValue;
215 }
216
217 Rect getDestinationBounds() {
218 return mDestinationBounds;
219 }
220
221 void setDestinationBounds(Rect destinationBounds) {
222 mDestinationBounds.set(destinationBounds);
223 }
224
225 void setCurrentValue(T value) {
226 mCurrentValue = value;
227 }
228
229 /**
230 * Updates the {@link #mEndValue}.
231 *
232 * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
233 * This is typically used when we receive a shelf height adjustment during the bounds
234 * animation. In which case we can update the end bounds and keep the existing animation
235 * running instead of cancelling it.
236 */
237 void updateEndValue(T endValue) {
238 mEndValue = endValue;
239 mStartValue = mCurrentValue;
240 }
241
242 SurfaceControl.Transaction newSurfaceControlTransaction() {
243 return mSurfaceControlTransactionFactory.getTransaction();
244 }
245
246 @VisibleForTesting
247 void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
248 mSurfaceControlTransactionFactory = factory;
249 }
250
251 abstract void applySurfaceControlTransaction(SurfaceControl leash,
252 SurfaceControl.Transaction tx, float fraction);
253
Winson Chung55701472020-03-04 19:30:30 -0800254 static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, boolean scheduleFinishPip,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800255 Rect destinationBounds, float startValue, float endValue) {
Winson Chung55701472020-03-04 19:30:30 -0800256 return new PipTransitionAnimator<Float>(leash, scheduleFinishPip, ANIM_TYPE_ALPHA,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800257 destinationBounds, startValue, endValue) {
258 @Override
259 void applySurfaceControlTransaction(SurfaceControl leash,
260 SurfaceControl.Transaction tx, float fraction) {
261 final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
262 setCurrentValue(alpha);
263 tx.setAlpha(leash, alpha);
264 if (Float.compare(fraction, FRACTION_START) == 0) {
265 // Ensure the start condition
266 final Rect bounds = getDestinationBounds();
267 tx.setPosition(leash, bounds.left, bounds.top)
268 .setWindowCrop(leash, bounds.width(), bounds.height());
269 }
270 tx.apply();
271 }
272 };
273 }
274
Winson Chung55701472020-03-04 19:30:30 -0800275 static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800276 Rect startValue, Rect endValue) {
277 // construct new Rect instances in case they are recycled
Winson Chung55701472020-03-04 19:30:30 -0800278 return new PipTransitionAnimator<Rect>(leash, scheduleFinishPip, ANIM_TYPE_BOUNDS,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800279 endValue, new Rect(startValue), new Rect(endValue)) {
280 private final Rect mTmpRect = new Rect();
281
282 private int getCastedFractionValue(float start, float end, float fraction) {
283 return (int) (start * (1 - fraction) + end * fraction + .5f);
284 }
285
286 @Override
287 void applySurfaceControlTransaction(SurfaceControl leash,
288 SurfaceControl.Transaction tx, float fraction) {
289 final Rect start = getStartValue();
290 final Rect end = getEndValue();
291 mTmpRect.set(
292 getCastedFractionValue(start.left, end.left, fraction),
293 getCastedFractionValue(start.top, end.top, fraction),
294 getCastedFractionValue(start.right, end.right, fraction),
295 getCastedFractionValue(start.bottom, end.bottom, fraction));
296 setCurrentValue(mTmpRect);
297 tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
298 .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
299 if (Float.compare(fraction, FRACTION_START) == 0) {
300 // Ensure the start condition
301 tx.setAlpha(leash, 1f);
302 }
303 tx.apply();
304 }
305 };
306 }
307 }
308
309 interface SurfaceControlTransactionFactory {
310 SurfaceControl.Transaction getTransaction();
311 }
312}