blob: cff2fadd76491ef75e05da55669fe4edba9d43f2 [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;
24import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080025import android.animation.Animator;
26import android.animation.ValueAnimator;
Winson Chung8bca9e42017-04-16 15:59:43 -070027import android.annotation.IntDef;
Winson Chungbaa7b722017-03-03 21:33:44 -080028import android.content.Context;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080029import android.graphics.Rect;
Wale Ogunwale2ba71292016-05-05 09:16:47 -070030import android.os.Handler;
Robert Carrf9aa2a92016-04-26 14:22:19 -070031import android.os.IBinder;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -080032import android.os.Debug;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080033import android.util.ArrayMap;
34import android.util.Slog;
Winson Chung4a526c12017-05-16 13:35:43 -070035import android.view.Choreographer;
Winson Chungbaa7b722017-03-03 21:33:44 -080036import android.view.animation.AnimationUtils;
37import android.view.animation.Interpolator;
Robert Carrf9aa2a92016-04-26 14:22:19 -070038import android.view.WindowManagerInternal;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080039
Winson Chung19953ca2017-04-11 11:19:23 -070040import com.android.internal.annotations.VisibleForTesting;
Winson Chung4a526c12017-05-16 13:35:43 -070041import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
Winson Chung19953ca2017-04-11 11:19:23 -070042
Winson Chung8bca9e42017-04-16 15:59:43 -070043import java.lang.annotation.Retention;
44import java.lang.annotation.RetentionPolicy;
45
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080046/**
47 * Enables animating bounds of objects.
48 *
49 * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
50 * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
51 * relaunching it would cause poorer experience), these class provides a way to directly animate
52 * the bounds of the resized object.
53 *
Winson Chung8bca9e42017-04-16 15:59:43 -070054 * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
Wale Ogunwale2ba71292016-05-05 09:16:47 -070055 *
Winson Chung4a526c12017-05-16 13:35:43 -070056 * NOTE: All calls to methods in this class should be done on the Animation thread
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080057 */
58public class BoundsAnimationController {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -080059 private static final boolean DEBUG_LOCAL = false;
60 private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM;
61 private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL
62 ? "BoundsAnimationController" : TAG_WM;
Wale Ogunwalece144522016-02-05 22:51:01 -080063 private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080064
Winson Chungbaa7b722017-03-03 21:33:44 -080065 private static final int DEFAULT_TRANSITION_DURATION = 425;
66
Winson Chung8bca9e42017-04-16 15:59:43 -070067 @Retention(RetentionPolicy.SOURCE)
68 @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
69 SCHEDULE_PIP_MODE_CHANGED_ON_END})
70 public @interface SchedulePipModeChangedState {}
71 /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
72 public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
73 /** Schedule a PiP mode changed callback when this animation starts. */
74 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
75 /** Schedule a PiP mode changed callback when this animation ends. */
76 public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
77
Wale Ogunwalece144522016-02-05 22:51:01 -080078 // Only accessed on UI thread.
Winson Chung8bca9e42017-04-16 15:59:43 -070079 private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -080080
Wale Ogunwale2ba71292016-05-05 09:16:47 -070081 private final class AppTransitionNotifier
82 extends WindowManagerInternal.AppTransitionListener implements Runnable {
Robert Carrf9aa2a92016-04-26 14:22:19 -070083
Wale Ogunwale2ba71292016-05-05 09:16:47 -070084 public void onAppTransitionCancelledLocked() {
Winson Chung87e5d552017-04-05 11:49:38 -070085 if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
86 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
Wale Ogunwale2ba71292016-05-05 09:16:47 -070087 animationFinished();
88 }
89 public void onAppTransitionFinishedLocked(IBinder token) {
Winson Chung87e5d552017-04-05 11:49:38 -070090 if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
91 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
Wale Ogunwale2ba71292016-05-05 09:16:47 -070092 animationFinished();
93 }
94 private void animationFinished() {
95 if (mFinishAnimationAfterTransition) {
96 mHandler.removeCallbacks(this);
Winson Chung87e5d552017-04-05 11:49:38 -070097 // This might end up calling into activity manager which will be bad since we have
98 // the window manager lock held at this point. Post a message to take care of the
99 // processing so we don't deadlock.
Wale Ogunwale2ba71292016-05-05 09:16:47 -0700100 mHandler.post(this);
101 }
102 }
103
104 @Override
105 public void run() {
106 for (int i = 0; i < mRunningAnimations.size(); i++) {
107 final BoundsAnimator b = mRunningAnimations.valueAt(i);
108 b.onAnimationEnd(null);
109 }
110 }
111 }
112
113 private final Handler mHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700114 private final AppTransition mAppTransition;
Wale Ogunwale2ba71292016-05-05 09:16:47 -0700115 private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
Winson Chungbaa7b722017-03-03 21:33:44 -0800116 private final Interpolator mFastOutSlowInInterpolator;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700117 private boolean mFinishAnimationAfterTransition = false;
Winson Chung4a526c12017-05-16 13:35:43 -0700118 private final AnimationHandler mAnimationHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700119
Robert Carrecc06b32017-04-18 14:25:10 -0700120 private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
121
Winson Chung4a526c12017-05-16 13:35:43 -0700122 BoundsAnimationController(Context context, AppTransition transition, Handler handler,
123 AnimationHandler animationHandler) {
Wale Ogunwale2ba71292016-05-05 09:16:47 -0700124 mHandler = handler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700125 mAppTransition = transition;
126 mAppTransition.registerListenerLocked(mAppTransitionNotifier);
Winson Chungbaa7b722017-03-03 21:33:44 -0800127 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
128 com.android.internal.R.interpolator.fast_out_slow_in);
Winson Chung4a526c12017-05-16 13:35:43 -0700129 mAnimationHandler = animationHandler;
Robert Carrf9aa2a92016-04-26 14:22:19 -0700130 }
131
Winson Chung19953ca2017-04-11 11:19:23 -0700132 @VisibleForTesting
133 final class BoundsAnimator extends ValueAnimator
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800134 implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
Winson Chung4a526c12017-05-16 13:35:43 -0700135
Winson Chung8bca9e42017-04-16 15:59:43 -0700136 private final BoundsAnimationTarget mTarget;
Winson Chung84a38342016-11-08 16:15:10 -0800137 private final Rect mFrom = new Rect();
138 private final Rect mTo = new Rect();
Robert Carr0d00c2e2016-02-29 17:45:02 -0800139 private final Rect mTmpRect = new Rect();
140 private final Rect mTmpTaskBounds = new Rect();
Winson Chung8bca9e42017-04-16 15:59:43 -0700141
142 // True if this this animation was canceled and will be replaced the another animation from
143 // the same {@link #BoundsAnimationTarget} target.
Winson Chung19953ca2017-04-11 11:19:23 -0700144 private boolean mSkipFinalResize;
Winson Chung5af42fc2017-03-24 17:11:33 -0700145 // True if this animation replaced a previous animation of the same
Winson Chung8bca9e42017-04-16 15:59:43 -0700146 // {@link #BoundsAnimationTarget} target.
Winson Chung5af42fc2017-03-24 17:11:33 -0700147 private final boolean mSkipAnimationStart;
Winson Chung8bca9e42017-04-16 15:59:43 -0700148 // True if this animation was canceled by the user, not as a part of a replacing animation
Winson Chung19953ca2017-04-11 11:19:23 -0700149 private boolean mSkipAnimationEnd;
Winson Chunge7ba6862017-05-24 12:13:33 -0700150
151 // True if the animation target is animating from the fullscreen. Only one of
152 // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the
153 // animation.
154 private boolean mMoveFromFullscreen;
Winson Chung8bca9e42017-04-16 15:59:43 -0700155 // True if the animation target should be moved to the fullscreen stack at the end of this
Winson Chunge7ba6862017-05-24 12:13:33 -0700156 // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be
157 // true at any time in the animation.
Winson Chung8bca9e42017-04-16 15:59:43 -0700158 private boolean mMoveToFullscreen;
159
160 // Whether to schedule PiP mode changes on animation start/end
161 private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800162
Robert Carr0d00c2e2016-02-29 17:45:02 -0800163 // Depending on whether we are animating from
164 // a smaller to a larger size
165 private final int mFrozenTaskWidth;
166 private final int mFrozenTaskHeight;
167
Winson Chunge7ba6862017-05-24 12:13:33 -0700168 // Timeout callback to ensure we continue the animation if waiting for resuming or app
169 // windows drawn fails
170 private final Runnable mResumeRunnable = () -> resume();
171
Winson Chung8bca9e42017-04-16 15:59:43 -0700172 BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
173 @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700174 boolean moveFromFullscreen, boolean moveToFullscreen,
175 boolean replacingExistingAnimation) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800176 super();
177 mTarget = target;
Winson Chung84a38342016-11-08 16:15:10 -0800178 mFrom.set(from);
179 mTo.set(to);
Winson Chung5af42fc2017-03-24 17:11:33 -0700180 mSkipAnimationStart = replacingExistingAnimation;
Winson Chung8bca9e42017-04-16 15:59:43 -0700181 mSchedulePipModeChangedState = schedulePipModeChangedState;
Winson Chunge7ba6862017-05-24 12:13:33 -0700182 mMoveFromFullscreen = moveFromFullscreen;
Winson Chung8bca9e42017-04-16 15:59:43 -0700183 mMoveToFullscreen = moveToFullscreen;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800184 addUpdateListener(this);
185 addListener(this);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800186
187 // If we are animating from smaller to larger, we want to change the task bounds
188 // to their final size immediately so we can use scaling to make the window
189 // larger. Likewise if we are going from bigger to smaller, we want to wait until
190 // the end so we don't have to upscale from the smaller finished size.
191 if (animatingToLargerSize()) {
192 mFrozenTaskWidth = mTo.width();
193 mFrozenTaskHeight = mTo.height();
194 } else {
195 mFrozenTaskWidth = mFrom.width();
196 mFrozenTaskHeight = mFrom.height();
197 }
198 }
199
Winson Chung5af42fc2017-03-24 17:11:33 -0700200 @Override
201 public void onAnimationStart(Animator animation) {
202 if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
Winson Chung8bca9e42017-04-16 15:59:43 -0700203 + " mSkipAnimationStart=" + mSkipAnimationStart
204 + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
Winson Chung5af42fc2017-03-24 17:11:33 -0700205 mFinishAnimationAfterTransition = false;
206 mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
207 mFrom.top + mFrozenTaskHeight);
208
Winson Chung1ec3e262017-06-09 12:01:20 -0700209 // Boost the thread priority of the animation thread while the bounds animation is
210 // running
211 updateBooster();
212
Winson Chung5af42fc2017-03-24 17:11:33 -0700213 // Ensure that we have prepared the target for animation before
214 // we trigger any size changes, so it can swap surfaces
215 // in to appropriate modes, or do as it wishes otherwise.
216 if (!mSkipAnimationStart) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700217 mTarget.onAnimationStart(mSchedulePipModeChangedState ==
218 SCHEDULE_PIP_MODE_CHANGED_ON_START);
Winson Chunge7ba6862017-05-24 12:13:33 -0700219
220 // When starting an animation from fullscreen, pause here and wait for the
221 // windows-drawn signal before we start the rest of the transition down into PiP.
222 if (mMoveFromFullscreen) {
223 pause();
224 }
Winson Chung5af42fc2017-03-24 17:11:33 -0700225 }
226
227 // Immediately update the task bounds if they have to become larger, but preserve
228 // the starting position so we don't jump at the beginning of the animation.
229 if (animatingToLargerSize()) {
230 mTarget.setPinnedStackSize(mFrom, mTmpRect);
Robert Carrecc06b32017-04-18 14:25:10 -0700231
232 // We pause the animation until the app has drawn at the new size.
233 // The target will notify us via BoundsAnimationController#resume.
234 // We do this here and pause the animation, rather than just defer starting it
235 // so we can enter the animating state and have WindowStateAnimator apply the
236 // correct logic to make this resize seamless.
237 if (mMoveToFullscreen) {
238 pause();
Robert Carrecc06b32017-04-18 14:25:10 -0700239 }
Winson Chung5af42fc2017-03-24 17:11:33 -0700240 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800241 }
242
243 @Override
Winson Chunge7ba6862017-05-24 12:13:33 -0700244 public void pause() {
245 if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn");
246 super.pause();
247 mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
248 }
249
250 @Override
Robert Carrecc06b32017-04-18 14:25:10 -0700251 public void resume() {
Winson Chunge7ba6862017-05-24 12:13:33 -0700252 if (DEBUG) Slog.d(TAG, "resume:");
Robert Carrecc06b32017-04-18 14:25:10 -0700253 mHandler.removeCallbacks(mResumeRunnable);
254 super.resume();
255 }
256
257 @Override
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800258 public void onAnimationUpdate(ValueAnimator animation) {
259 final float value = (Float) animation.getAnimatedValue();
260 final float remains = 1 - value;
Wale Ogunwalece144522016-02-05 22:51:01 -0800261 mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
262 mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
263 mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
264 mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800265 if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
266 + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
267 + " remains=" + remains);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800268
Robert Carrc7294602016-05-13 11:32:05 -0700269 mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
270 mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
Robert Carr0d00c2e2016-02-29 17:45:02 -0800271
Robert Carrc7294602016-05-13 11:32:05 -0700272 if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800273 // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
274 // any further animation.
Winson Chung87e5d552017-04-05 11:49:38 -0700275 if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
Winson Chung19953ca2017-04-11 11:19:23 -0700276
Winson Chung8bca9e42017-04-16 15:59:43 -0700277 // If we have already scheduled a PiP mode changed at the start of the animation,
278 // then we need to clean up and schedule one at the end, since we have canceled the
279 // animation to the final state.
280 if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
281 mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
282 }
283
Winson Chung19953ca2017-04-11 11:19:23 -0700284 // Since we are cancelling immediately without a replacement animation, send the
285 // animation end to maintain callback parity, but also skip any further resizes
Winson Chung8bca9e42017-04-16 15:59:43 -0700286 cancelAndCallAnimationEnd();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800287 }
288 }
289
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800290 @Override
291 public void onAnimationEnd(Animator animation) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800292 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
Winson Chung19953ca2017-04-11 11:19:23 -0700293 + " mSkipFinalResize=" + mSkipFinalResize
Winson Chung87e5d552017-04-05 11:49:38 -0700294 + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
Winson Chung8bca9e42017-04-16 15:59:43 -0700295 + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
296 + " callers=" + Debug.getCallers(2));
Jaewan Kimb0033642016-04-22 18:41:37 +0900297
Robert Carrf9aa2a92016-04-26 14:22:19 -0700298 // There could be another animation running. For example in the
299 // move to fullscreen case, recents will also be closing while the
300 // previous task will be taking its place in the fullscreen stack.
301 // we have to ensure this is completed before we finish the animation
302 // and take our place in the fullscreen stack.
303 if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
304 mFinishAnimationAfterTransition = true;
305 return;
306 }
307
Winson Chung8bca9e42017-04-16 15:59:43 -0700308 if (!mSkipAnimationEnd) {
309 // If this animation has already scheduled the picture-in-picture mode on start, and
310 // we are not skipping the final resize due to being canceled, then move the PiP to
311 // fullscreen once the animation ends
312 if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
313 + " moveToFullscreen=" + mMoveToFullscreen);
314 mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
315 SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
316 mMoveToFullscreen);
Winson Chung19953ca2017-04-11 11:19:23 -0700317 }
318
Winson Chung8bca9e42017-04-16 15:59:43 -0700319 // Clean up this animation
320 removeListener(this);
321 removeUpdateListener(this);
322 mRunningAnimations.remove(mTarget);
Winson Chung1ec3e262017-06-09 12:01:20 -0700323
324 // Reset the thread priority of the animation thread after the bounds animation is done
325 updateBooster();
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800326 }
327
328 @Override
329 public void onAnimationCancel(Animator animation) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700330 // Always skip the final resize when the animation is canceled
331 mSkipFinalResize = true;
332 mMoveToFullscreen = false;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800333 }
334
Winson Chung8bca9e42017-04-16 15:59:43 -0700335 private void cancelAndCallAnimationEnd() {
336 if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
337 mSkipAnimationEnd = false;
338 super.cancel();
Winson Chung19953ca2017-04-11 11:19:23 -0700339 }
340
Wale Ogunwalece144522016-02-05 22:51:01 -0800341 @Override
342 public void cancel() {
Winson Chung19953ca2017-04-11 11:19:23 -0700343 if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
Winson Chung8bca9e42017-04-16 15:59:43 -0700344 mSkipAnimationEnd = true;
Wale Ogunwalece144522016-02-05 22:51:01 -0800345 super.cancel();
346 }
347
Winson Chung8bca9e42017-04-16 15:59:43 -0700348 /**
349 * @return true if the animation target is the same as the input bounds.
350 */
Winson Chung5af42fc2017-03-24 17:11:33 -0700351 boolean isAnimatingTo(Rect bounds) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800352 return mTo.equals(bounds);
353 }
354
Winson Chung8bca9e42017-04-16 15:59:43 -0700355 /**
356 * @return true if we are animating to a larger surface size
357 */
358 @VisibleForTesting
359 boolean animatingToLargerSize() {
360 // TODO: Fix this check for aspect ratio changes
361 return (mFrom.width() * mFrom.height() <= mTo.width() * mTo.height());
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800362 }
363
364 @Override
365 public void onAnimationRepeat(Animator animation) {
Winson Chung5af42fc2017-03-24 17:11:33 -0700366 // Do nothing
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800367 }
Winson Chung4a526c12017-05-16 13:35:43 -0700368
369 @Override
370 public AnimationHandler getAnimationHandler() {
371 if (mAnimationHandler != null) {
372 return mAnimationHandler;
373 }
374 return super.getAnimationHandler();
375 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800376 }
377
Winson Chung8bca9e42017-04-16 15:59:43 -0700378 public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
379 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700380 boolean moveFromFullscreen, boolean moveToFullscreen) {
Winson Chung8bca9e42017-04-16 15:59:43 -0700381 animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700382 moveFromFullscreen, moveToFullscreen);
Winson Chung19953ca2017-04-11 11:19:23 -0700383 }
384
385 @VisibleForTesting
Winson Chung8bca9e42017-04-16 15:59:43 -0700386 BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
387 int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
Winson Chunge7ba6862017-05-24 12:13:33 -0700388 boolean moveFromFullscreen, boolean moveToFullscreen) {
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800389 final BoundsAnimator existing = mRunningAnimations.get(target);
Wale Ogunwalece144522016-02-05 22:51:01 -0800390 final boolean replacing = existing != null;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800391
392 if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
Winson Chung8bca9e42017-04-16 15:59:43 -0700393 + " schedulePipModeChangedState=" + schedulePipModeChangedState
394 + " replacing=" + replacing);
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800395
Wale Ogunwalece144522016-02-05 22:51:01 -0800396 if (replacing) {
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800397 if (existing.isAnimatingTo(to)) {
Winson Chung19953ca2017-04-11 11:19:23 -0700398 // Just let the current animation complete if it has the same destination as the
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800399 // one we are trying to start.
400 if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
401 + " ignoring...");
Winson Chung8bca9e42017-04-16 15:59:43 -0700402
Winson Chung19953ca2017-04-11 11:19:23 -0700403 return existing;
Wale Ogunwale5658e4b2016-02-12 12:22:19 -0800404 }
Winson Chung8bca9e42017-04-16 15:59:43 -0700405
406 // Update the PiP callback states if we are replacing the animation
407 if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
408 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
409 if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
410 + " existing deferred state");
411 } else {
412 if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
413 + " on start already processed, schedule deferred update on end");
414 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
415 }
416 } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
417 if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
418 if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
419 + " callback on start will be processed");
420 } else {
421 if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
422 + " existing deferred state");
423 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
424 }
425 }
426
427 // Since we are replacing, we skip both animation start and end callbacks
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800428 existing.cancel();
429 }
Winson Chung8bca9e42017-04-16 15:59:43 -0700430 final BoundsAnimator animator = new BoundsAnimator(target, from, to,
Winson Chunge7ba6862017-05-24 12:13:33 -0700431 schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800432 mRunningAnimations.put(target, animator);
433 animator.setFloatValues(0f, 1f);
Wale Ogunwalee75a9ad2016-03-18 20:43:49 -0700434 animator.setDuration((animationDuration != -1 ? animationDuration
Winson Chungbaa7b722017-03-03 21:33:44 -0800435 : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
436 animator.setInterpolator(mFastOutSlowInInterpolator);
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800437 animator.start();
Winson Chung19953ca2017-04-11 11:19:23 -0700438 return animator;
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800439 }
Robert Carrecc06b32017-04-18 14:25:10 -0700440
Winson Chunge7ba6862017-05-24 12:13:33 -0700441 public Handler getHandler() {
442 return mHandler;
443 }
444
445 public void onAllWindowsDrawn() {
446 if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:");
447 mHandler.post(this::resume);
448 }
449
Robert Carrecc06b32017-04-18 14:25:10 -0700450 private void resume() {
451 for (int i = 0; i < mRunningAnimations.size(); i++) {
452 final BoundsAnimator b = mRunningAnimations.valueAt(i);
453 b.resume();
454 }
455 }
Winson Chung1ec3e262017-06-09 12:01:20 -0700456
457 private void updateBooster() {
458 WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning(
459 !mRunningAnimations.isEmpty());
460 }
Filip Gruszczynski84fa3352016-01-25 16:28:49 -0800461}