blob: 7953ee430934941e1059fd564df5388931807368 [file] [log] [blame]
Filip Gruszczynski84fa3352016-01-25 16:28:49 -08001/*
2 * Copyright (C) 2016 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.server.wm;
18
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080019import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
22
Winson Chung4a526c12017-05-16 13:35:43 -070023import android.animation.AnimationHandler;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080024import android.animation.Animator;
25import android.animation.ValueAnimator;
Winson Chung8bca9e42017-04-16 15:59:43 -070026import android.annotation.IntDef;
Winson Chungbaa7b722017-03-03 21:33:44 -080027import android.content.Context;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080028import android.graphics.Rect;
Wale Ogunwale2ba71292016-05-05 09:16:47 -070029import android.os.Handler;
Robert Carrf9aa2a92016-04-26 14:22:19 -070030import android.os.IBinder;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -080031import android.os.Debug;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080032import android.util.ArrayMap;
33import android.util.Slog;
Winson Chungbaa7b722017-03-03 21:33:44 -080034import android.view.animation.AnimationUtils;
35import android.view.animation.Interpolator;
Robert Carrf9aa2a92016-04-26 14:22:19 -070036import android.view.WindowManagerInternal;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080037
Winson Chung19953ca2017-04-11 11:19:23 -070038import com.android.internal.annotations.VisibleForTesting;
39
Winson Chung8bca9e42017-04-16 15:59:43 -070040import java.lang.annotation.Retention;
41import java.lang.annotation.RetentionPolicy;
42
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080043/**
44 * Enables animating bounds of objects.
45 *
46 * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
47 * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
48 * relaunching it would cause poorer experience), these class provides a way to directly animate
49 * the bounds of the resized object.
50 *
Winson Chung8bca9e42017-04-16 15:59:43 -070051 * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
Wale Ogunwale2ba71292016-05-05 09:16:47 -070052 *
Winson Chung4a526c12017-05-16 13:35:43 -070053 * NOTE: All calls to methods in this class should be done on the Animation thread
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080054 */
55public class BoundsAnimationController {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -080056 private static final boolean DEBUG_LOCAL = false;
57 private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM;
58 private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL
59 ? "BoundsAnimationController" : TAG_WM;
Wale Ogunwalece144522016-02-05 22:51:01 -080060 private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080061
Winson Chungbaa7b722017-03-03 21:33:44 -080062 private static final int DEFAULT_TRANSITION_DURATION = 425;
63
Winson Chung8bca9e42017-04-16 15:59:43 -070064 @Retention(RetentionPolicy.SOURCE)
65 @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
66 SCHEDULE_PIP_MODE_CHANGED_ON_END})
67 public @interface SchedulePipModeChangedState {}
68 /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
69 public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
70 /** Schedule a PiP mode changed callback when this animation starts. */
71 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
72 /** Schedule a PiP mode changed callback when this animation ends. */
73 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
74
Wale Ogunwalece144522016-02-05 22:51:01 -080075 // Only accessed on UI thread.
Winson Chung8bca9e42017-04-16 15:59:43 -070076 private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080077
Wale Ogunwale2ba71292016-05-05 09:16:47 -070078 private final class AppTransitionNotifier
79 extends WindowManagerInternal.AppTransitionListener implements Runnable {
Robert Carrf9aa2a92016-04-26 14:22:19 -070080
Wale Ogunwale2ba71292016-05-05 09:16:47 -070081 public void onAppTransitionCancelledLocked() {
Winson Chung87e5d552017-04-05 11:49:38 -070082 if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
83 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
Wale Ogunwale2ba71292016-05-05 09:16:47 -070084 animationFinished();
85 }
86 public void onAppTransitionFinishedLocked(IBinder token) {
Winson Chung87e5d552017-04-05 11:49:38 -070087 if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
88 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
Wale Ogunwale2ba71292016-05-05 09:16:47 -070089 animationFinished();
90 }
91 private void animationFinished() {
92 if (mFinishAnimationAfterTransition) {
93 mHandler.removeCallbacks(this);
Winson Chung87e5d552017-04-05 11:49:38 -070094 // This might end up calling into activity manager which will be bad since we have
95 // the window manager lock held at this point. Post a message to take care of the
96 // processing so we don't deadlock.
Wale Ogunwale2ba71292016-05-05 09:16:47 -070097 mHandler.post(this);
98 }
99 }
100
101 @Override
102 public void run() {
103 for (int i = 0; i < mRunningAnimations.size(); i++) {
104 final BoundsAnimator b = mRunningAnimations.valueAt(i);
105 b.onAnimationEnd(null);
106 }
107 }
108 }
109
110 private final Handler mHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700111 private final AppTransition mAppTransition;
Wale Ogunwale2ba71292016-05-05 09:16:47 -0700112 private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
Winson Chungbaa7b722017-03-03 21:33:44 -0800113 private final Interpolator mFastOutSlowInInterpolator;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700114 private boolean mFinishAnimationAfterTransition = false;
Winson Chung4a526c12017-05-16 13:35:43 -0700115 private final AnimationHandler mAnimationHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700116
Robert Carrecc06b32017-04-18 14:25:10 -0700117 private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
118
Winson Chung4a526c12017-05-16 13:35:43 -0700119 BoundsAnimationController(Context context, AppTransition transition, Handler handler,
120 AnimationHandler animationHandler) {
Wale Ogunwale2ba71292016-05-05 09:16:47 -0700121 mHandler = handler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700122 mAppTransition = transition;
123 mAppTransition.registerListenerLocked(mAppTransitionNotifier);
Winson Chungbaa7b722017-03-03 21:33:44 -0800124 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
125 com.android.internal.R.interpolator.fast_out_slow_in);
Winson Chung4a526c12017-05-16 13:35:43 -0700126 mAnimationHandler = animationHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700127 }
128
Winson Chung19953ca2017-04-11 11:19:23 -0700129 @VisibleForTesting
130 final class BoundsAnimator extends ValueAnimator
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800131 implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
Winson Chung4a526c12017-05-16 13:35:43 -0700132
Winson Chung8bca9e42017-04-16 15:59:43 -0700133 private final BoundsAnimationTarget mTarget;
Winson Chung84a38342016-11-08 16:15:10 -0800134 private final Rect mFrom = new Rect();
135 private final Rect mTo = new Rect();
Robert Carr0d00c2e2016-02-29 17:45:02 -0800136 private final Rect mTmpRect = new Rect();
137 private final Rect mTmpTaskBounds = new Rect();
Winson Chung8bca9e42017-04-16 15:59:43 -0700138
139 // True if this this animation was canceled and will be replaced the another animation from
140 // the same {@link #BoundsAnimationTarget} target.
Winson Chung19953ca2017-04-11 11:19:23 -0700141 private boolean mSkipFinalResize;
Winson Chung8bca9e42017-04-16 15:59:43 -0700142 // True if this animation was canceled by the user, not as a part of a replacing animation
Winson Chung19953ca2017-04-11 11:19:23 -0700143 private boolean mSkipAnimationEnd;
Winson Chunge7ba6862017-05-24 12:13:33 -0700144
145 // True if the animation target is animating from the fullscreen. Only one of
146 // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the
147 // animation.
148 private boolean mMoveFromFullscreen;
Winson Chung8bca9e42017-04-16 15:59:43 -0700149 // True if the animation target should be moved to the fullscreen stack at the end of this
Winson Chunge7ba6862017-05-24 12:13:33 -0700150 // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be
151 // true at any time in the animation.
Winson Chung8bca9e42017-04-16 15:59:43 -0700152 private boolean mMoveToFullscreen;
153
154 // Whether to schedule PiP mode changes on animation start/end
155 private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
Winson Chungab76bbc2017-08-14 13:33:51 -0700156 private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800157
Robert Carr0d00c2e2016-02-29 17:45:02 -0800158 // Depending on whether we are animating from
159 // a smaller to a larger size
160 private final int mFrozenTaskWidth;
161 private final int mFrozenTaskHeight;
162
Winson Chunge7ba6862017-05-24 12:13:33 -0700163 // Timeout callback to ensure we continue the animation if waiting for resuming or app
164 // windows drawn fails
165 private final Runnable mResumeRunnable = () -> resume();
166
Winson Chung8bca9e42017-04-16 15:59:43 -0700167 BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
168 @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chungab76bbc2017-08-14 13:33:51 -0700169 @SchedulePipModeChangedState int prevShedulePipModeChangedState,
170 boolean moveFromFullscreen, boolean moveToFullscreen) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800171 super();
172 mTarget = target;
Winson Chung84a38342016-11-08 16:15:10 -0800173 mFrom.set(from);
174 mTo.set(to);
Winson Chung8bca9e42017-04-16 15:59:43 -0700175 mSchedulePipModeChangedState = schedulePipModeChangedState;
Winson Chungab76bbc2017-08-14 13:33:51 -0700176 mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
Winson Chunge7ba6862017-05-24 12:13:33 -0700177 mMoveFromFullscreen = moveFromFullscreen;
Winson Chung8bca9e42017-04-16 15:59:43 -0700178 mMoveToFullscreen = moveToFullscreen;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800179 addUpdateListener(this);
180 addListener(this);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800181
182 // If we are animating from smaller to larger, we want to change the task bounds
183 // to their final size immediately so we can use scaling to make the window
184 // larger. Likewise if we are going from bigger to smaller, we want to wait until
185 // the end so we don't have to upscale from the smaller finished size.
186 if (animatingToLargerSize()) {
187 mFrozenTaskWidth = mTo.width();
188 mFrozenTaskHeight = mTo.height();
189 } else {
190 mFrozenTaskWidth = mFrom.width();
191 mFrozenTaskHeight = mFrom.height();
192 }
193 }
194
Winson Chung5af42fc2017-03-24 17:11:33 -0700195 @Override
196 public void onAnimationStart(Animator animation) {
197 if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
Winson Chungab76bbc2017-08-14 13:33:51 -0700198 + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
Winson Chung8bca9e42017-04-16 15:59:43 -0700199 + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
Winson Chung5af42fc2017-03-24 17:11:33 -0700200 mFinishAnimationAfterTransition = false;
201 mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
202 mFrom.top + mFrozenTaskHeight);
203
Winson Chung1ec3e262017-06-09 12:01:20 -0700204 // Boost the thread priority of the animation thread while the bounds animation is
205 // running
206 updateBooster();
207
Winson Chungab76bbc2017-08-14 13:33:51 -0700208 // Ensure that we have prepared the target for animation before we trigger any size
209 // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
210 // otherwise.
211 if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700212 mTarget.onAnimationStart(mSchedulePipModeChangedState ==
Winson Chungab76bbc2017-08-14 13:33:51 -0700213 SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
Winson Chunge7ba6862017-05-24 12:13:33 -0700214
215 // When starting an animation from fullscreen, pause here and wait for the
216 // windows-drawn signal before we start the rest of the transition down into PiP.
217 if (mMoveFromFullscreen) {
218 pause();
219 }
Winson Chungab76bbc2017-08-14 13:33:51 -0700220 } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
221 mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
222 // We are replacing a running animation into PiP, but since it hasn't completed, the
223 // client will not currently receive any picture-in-picture mode change callbacks.
224 // However, we still need to report to them that they are leaving PiP, so this will
225 // force an update via a mode changed callback.
226 mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
227 true /* forceUpdate */);
Winson Chung5af42fc2017-03-24 17:11:33 -0700228 }
229
230 // Immediately update the task bounds if they have to become larger, but preserve
231 // the starting position so we don't jump at the beginning of the animation.
232 if (animatingToLargerSize()) {
233 mTarget.setPinnedStackSize(mFrom, mTmpRect);
Robert Carrecc06b32017-04-18 14:25:10 -0700234
235 // We pause the animation until the app has drawn at the new size.
236 // The target will notify us via BoundsAnimationController#resume.
237 // We do this here and pause the animation, rather than just defer starting it
238 // so we can enter the animating state and have WindowStateAnimator apply the
239 // correct logic to make this resize seamless.
240 if (mMoveToFullscreen) {
241 pause();
Robert Carrecc06b32017-04-18 14:25:10 -0700242 }
Winson Chung5af42fc2017-03-24 17:11:33 -0700243 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800244 }
245
246 @Override
Winson Chunge7ba6862017-05-24 12:13:33 -0700247 public void pause() {
248 if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn");
249 super.pause();
250 mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
251 }
252
253 @Override
Robert Carrecc06b32017-04-18 14:25:10 -0700254 public void resume() {
Winson Chunge7ba6862017-05-24 12:13:33 -0700255 if (DEBUG) Slog.d(TAG, "resume:");
Robert Carrecc06b32017-04-18 14:25:10 -0700256 mHandler.removeCallbacks(mResumeRunnable);
257 super.resume();
258 }
259
260 @Override
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800261 public void onAnimationUpdate(ValueAnimator animation) {
262 final float value = (Float) animation.getAnimatedValue();
263 final float remains = 1 - value;
Wale Ogunwalece144522016-02-05 22:51:01 -0800264 mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
265 mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
266 mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
267 mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800268 if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
269 + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
270 + " remains=" + remains);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800271
Robert Carrc7294602016-05-13 11:32:05 -0700272 mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
273 mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800274
Robert Carrc7294602016-05-13 11:32:05 -0700275 if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800276 // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
277 // any further animation.
Winson Chung87e5d552017-04-05 11:49:38 -0700278 if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
Winson Chung19953ca2017-04-11 11:19:23 -0700279
Winson Chung8bca9e42017-04-16 15:59:43 -0700280 // If we have already scheduled a PiP mode changed at the start of the animation,
281 // then we need to clean up and schedule one at the end, since we have canceled the
282 // animation to the final state.
283 if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
284 mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
285 }
286
Winson Chung19953ca2017-04-11 11:19:23 -0700287 // Since we are cancelling immediately without a replacement animation, send the
288 // animation end to maintain callback parity, but also skip any further resizes
Winson Chung8bca9e42017-04-16 15:59:43 -0700289 cancelAndCallAnimationEnd();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800290 }
291 }
292
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800293 @Override
294 public void onAnimationEnd(Animator animation) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800295 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
Winson Chung19953ca2017-04-11 11:19:23 -0700296 + " mSkipFinalResize=" + mSkipFinalResize
Winson Chung87e5d552017-04-05 11:49:38 -0700297 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
Winson Chung8bca9e42017-04-16 15:59:43 -0700298 + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
299 + " callers=" + Debug.getCallers(2));
Jaewan Kimb0033642016-04-22 18:41:37 +0900300
Robert Carrf9aa2a92016-04-26 14:22:19 -0700301 // There could be another animation running. For example in the
302 // move to fullscreen case, recents will also be closing while the
303 // previous task will be taking its place in the fullscreen stack.
304 // we have to ensure this is completed before we finish the animation
305 // and take our place in the fullscreen stack.
306 if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
307 mFinishAnimationAfterTransition = true;
308 return;
309 }
310
Winson Chung8bca9e42017-04-16 15:59:43 -0700311 if (!mSkipAnimationEnd) {
312 // If this animation has already scheduled the picture-in-picture mode on start, and
313 // we are not skipping the final resize due to being canceled, then move the PiP to
314 // fullscreen once the animation ends
315 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
316 + " moveToFullscreen=" + mMoveToFullscreen);
317 mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
318 SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
319 mMoveToFullscreen);
Winson Chung19953ca2017-04-11 11:19:23 -0700320 }
321
Winson Chung8bca9e42017-04-16 15:59:43 -0700322 // Clean up this animation
323 removeListener(this);
324 removeUpdateListener(this);
325 mRunningAnimations.remove(mTarget);
Winson Chung1ec3e262017-06-09 12:01:20 -0700326
327 // Reset the thread priority of the animation thread after the bounds animation is done
328 updateBooster();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800329 }
330
331 @Override
332 public void onAnimationCancel(Animator animation) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700333 // Always skip the final resize when the animation is canceled
334 mSkipFinalResize = true;
335 mMoveToFullscreen = false;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800336 }
337
Winson Chung8bca9e42017-04-16 15:59:43 -0700338 private void cancelAndCallAnimationEnd() {
339 if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
340 mSkipAnimationEnd = false;
341 super.cancel();
Winson Chung19953ca2017-04-11 11:19:23 -0700342 }
343
Wale Ogunwalece144522016-02-05 22:51:01 -0800344 @Override
345 public void cancel() {
Winson Chung19953ca2017-04-11 11:19:23 -0700346 if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
Winson Chung8bca9e42017-04-16 15:59:43 -0700347 mSkipAnimationEnd = true;
Wale Ogunwalece144522016-02-05 22:51:01 -0800348 super.cancel();
349 }
350
Winson Chung8bca9e42017-04-16 15:59:43 -0700351 /**
352 * @return true if the animation target is the same as the input bounds.
353 */
Winson Chung5af42fc2017-03-24 17:11:33 -0700354 boolean isAnimatingTo(Rect bounds) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800355 return mTo.equals(bounds);
356 }
357
Winson Chung8bca9e42017-04-16 15:59:43 -0700358 /**
359 * @return true if we are animating to a larger surface size
360 */
361 @VisibleForTesting
362 boolean animatingToLargerSize() {
363 // TODO: Fix this check for aspect ratio changes
364 return (mFrom.width() * mFrom.height() <= mTo.width() * mTo.height());
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800365 }
366
367 @Override
368 public void onAnimationRepeat(Animator animation) {
Winson Chung5af42fc2017-03-24 17:11:33 -0700369 // Do nothing
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800370 }
Winson Chung4a526c12017-05-16 13:35:43 -0700371
372 @Override
373 public AnimationHandler getAnimationHandler() {
374 if (mAnimationHandler != null) {
375 return mAnimationHandler;
376 }
377 return super.getAnimationHandler();
378 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800379 }
380
Winson Chung8bca9e42017-04-16 15:59:43 -0700381 public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
382 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700383 boolean moveFromFullscreen, boolean moveToFullscreen) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700384 animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700385 moveFromFullscreen, moveToFullscreen);
Winson Chung19953ca2017-04-11 11:19:23 -0700386 }
387
388 @VisibleForTesting
Winson Chung8bca9e42017-04-16 15:59:43 -0700389 BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
390 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700391 boolean moveFromFullscreen, boolean moveToFullscreen) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800392 final BoundsAnimator existing = mRunningAnimations.get(target);
Wale Ogunwalece144522016-02-05 22:51:01 -0800393 final boolean replacing = existing != null;
Winson Chungab76bbc2017-08-14 13:33:51 -0700394 @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
395 NO_PIP_MODE_CHANGED_CALLBACKS;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800396
397 if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
Winson Chung8bca9e42017-04-16 15:59:43 -0700398 + " schedulePipModeChangedState=" + schedulePipModeChangedState
399 + " replacing=" + replacing);
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800400
Wale Ogunwalece144522016-02-05 22:51:01 -0800401 if (replacing) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800402 if (existing.isAnimatingTo(to)) {
Winson Chung19953ca2017-04-11 11:19:23 -0700403 // Just let the current animation complete if it has the same destination as the
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800404 // one we are trying to start.
405 if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
406 + " ignoring...");
Winson Chung8bca9e42017-04-16 15:59:43 -0700407
Winson Chung19953ca2017-04-11 11:19:23 -0700408 return existing;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800409 }
Winson Chung8bca9e42017-04-16 15:59:43 -0700410
Winson Chungab76bbc2017-08-14 13:33:51 -0700411 // Save the previous state
412 prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
413
Winson Chung8bca9e42017-04-16 15:59:43 -0700414 // Update the PiP callback states if we are replacing the animation
415 if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
416 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
417 if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
418 + " existing deferred state");
419 } else {
420 if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
421 + " on start already processed, schedule deferred update on end");
422 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
423 }
424 } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
425 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
426 if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
427 + " callback on start will be processed");
428 } else {
429 if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
430 + " existing deferred state");
431 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
432 }
433 }
434
435 // Since we are replacing, we skip both animation start and end callbacks
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800436 existing.cancel();
437 }
Winson Chung8bca9e42017-04-16 15:59:43 -0700438 final BoundsAnimator animator = new BoundsAnimator(target, from, to,
Winson Chungab76bbc2017-08-14 13:33:51 -0700439 schedulePipModeChangedState, prevSchedulePipModeChangedState,
440 moveFromFullscreen, moveToFullscreen);
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800441 mRunningAnimations.put(target, animator);
442 animator.setFloatValues(0f, 1f);
Wale Ogunwalee75a9ad2016-03-18 20:43:49 -0700443 animator.setDuration((animationDuration != -1 ? animationDuration
Winson Chungbaa7b722017-03-03 21:33:44 -0800444 : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
445 animator.setInterpolator(mFastOutSlowInInterpolator);
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800446 animator.start();
Winson Chung19953ca2017-04-11 11:19:23 -0700447 return animator;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800448 }
Robert Carrecc06b32017-04-18 14:25:10 -0700449
Winson Chunge7ba6862017-05-24 12:13:33 -0700450 public Handler getHandler() {
451 return mHandler;
452 }
453
454 public void onAllWindowsDrawn() {
455 if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:");
456 mHandler.post(this::resume);
457 }
458
Robert Carrecc06b32017-04-18 14:25:10 -0700459 private void resume() {
460 for (int i = 0; i < mRunningAnimations.size(); i++) {
461 final BoundsAnimator b = mRunningAnimations.valueAt(i);
462 b.resume();
463 }
464 }
Winson Chung1ec3e262017-06-09 12:01:20 -0700465
466 private void updateBooster() {
467 WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning(
468 !mRunningAnimations.isEmpty());
469 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800470}