2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.wm;
19 import static android.view.WindowManagerInternal.AppTransitionListener;
20 import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
21 import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
22 import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
23 import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
24 import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
25 import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
26 import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
27 import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
28 import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
29 import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
30 import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation;
31 import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
32 import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
33 import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
34 import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation;
35 import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
36 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation;
37 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
38 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation;
39 import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
40 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
41 import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
42 import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
43 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
44 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
45 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
46 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
47 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
48 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
50 import android.annotation.Nullable;
51 import android.content.Context;
52 import android.content.res.Configuration;
53 import android.graphics.Bitmap;
54 import android.graphics.Path;
55 import android.graphics.Rect;
56 import android.os.Debug;
57 import android.os.IBinder;
58 import android.os.IRemoteCallback;
59 import android.os.RemoteException;
60 import android.util.ArraySet;
61 import android.util.Slog;
62 import android.util.SparseArray;
63 import android.view.AppTransitionAnimationSpec;
64 import android.view.IAppTransitionAnimationSpecsFuture;
65 import android.view.WindowManager;
66 import android.view.animation.AlphaAnimation;
67 import android.view.animation.Animation;
68 import android.view.animation.AnimationSet;
69 import android.view.animation.AnimationUtils;
70 import android.view.animation.ClipRectAnimation;
71 import android.view.animation.Interpolator;
72 import android.view.animation.PathInterpolator;
73 import android.view.animation.ScaleAnimation;
74 import android.view.animation.TranslateAnimation;
76 import com.android.internal.util.DumpUtils.Dump;
77 import com.android.server.AttributeCache;
78 import com.android.server.wm.WindowManagerService.H;
79 import com.android.server.wm.animation.ClipRectLRAnimation;
80 import com.android.server.wm.animation.ClipRectTBAnimation;
81 import com.android.server.wm.animation.CurvedTranslateAnimation;
83 import java.io.PrintWriter;
84 import java.util.ArrayList;
85 import java.util.concurrent.ExecutorService;
86 import java.util.concurrent.Executors;
88 // State management of app transitions. When we are preparing for a
89 // transition, mNextAppTransition will be the kind of transition to
90 // perform or TRANSIT_NONE if we are not waiting. If we are waiting,
91 // mOpeningApps and mClosingApps are the lists of tokens that will be
92 // made visible or hidden at the next transition.
93 public class AppTransition implements Dump {
94 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM;
95 private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
97 /** Not set up for a transition. */
98 public static final int TRANSIT_UNSET = -1;
99 /** No animation for transition. */
100 public static final int TRANSIT_NONE = 0;
101 /** A window in a new activity is being opened on top of an existing one in the same task. */
102 public static final int TRANSIT_ACTIVITY_OPEN = 6;
103 /** The window in the top-most activity is being closed to reveal the
104 * previous activity in the same task. */
105 public static final int TRANSIT_ACTIVITY_CLOSE = 7;
106 /** A window in a new task is being opened on top of an existing one
107 * in another activity's task. */
108 public static final int TRANSIT_TASK_OPEN = 8;
109 /** A window in the top-most activity is being closed to reveal the
110 * previous activity in a different task. */
111 public static final int TRANSIT_TASK_CLOSE = 9;
112 /** A window in an existing task is being displayed on top of an existing one
113 * in another activity's task. */
114 public static final int TRANSIT_TASK_TO_FRONT = 10;
115 /** A window in an existing task is being put below all other tasks. */
116 public static final int TRANSIT_TASK_TO_BACK = 11;
117 /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
118 * does, effectively closing the wallpaper. */
119 public static final int TRANSIT_WALLPAPER_CLOSE = 12;
120 /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
121 * effectively opening the wallpaper. */
122 public static final int TRANSIT_WALLPAPER_OPEN = 13;
123 /** A window in a new activity is being opened on top of an existing one, and both are on top
124 * of the wallpaper. */
125 public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
126 /** The window in the top-most activity is being closed to reveal the previous activity, and
127 * both are on top of the wallpaper. */
128 public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
129 /** A window in a new task is being opened behind an existing one in another activity's task.
130 * The new window will show briefly and then be gone. */
131 public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
132 /** A window in a task is being animated in-place. */
133 public static final int TRANSIT_TASK_IN_PLACE = 17;
134 /** An activity is being relaunched (e.g. due to configuration change). */
135 public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
136 /** A task is being docked from recents. */
137 public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
139 /** Fraction of animation at which the recents thumbnail stays completely transparent */
140 private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
141 /** Fraction of animation at which the recents thumbnail becomes completely transparent */
142 private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
144 static final int DEFAULT_APP_TRANSITION_DURATION = 336;
146 /** Interpolator to be used for animations that respond directly to a touch */
147 static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
148 new PathInterpolator(0.3f, 0f, 0.1f, 1f);
150 private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
151 new PathInterpolator(0.85f, 0f, 1f, 1f);
154 * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
155 * involved, to make it more understandable.
157 private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
158 private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
159 private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
161 private final Context mContext;
162 private final WindowManagerService mService;
164 private int mNextAppTransition = TRANSIT_UNSET;
166 private static final int NEXT_TRANSIT_TYPE_NONE = 0;
167 private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
168 private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
169 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
170 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
171 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
172 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
173 private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7;
174 private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8;
175 private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
177 // These are the possible states for the enter/exit activities during a thumbnail transition
178 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
179 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
180 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
181 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
183 private String mNextAppTransitionPackage;
184 // Used for thumbnail transitions. True if we're scaling up, false if scaling down
185 private boolean mNextAppTransitionScaleUp;
186 private IRemoteCallback mNextAppTransitionCallback;
187 private IRemoteCallback mNextAppTransitionFutureCallback;
188 private IRemoteCallback mAnimationFinishedCallback;
189 private int mNextAppTransitionEnter;
190 private int mNextAppTransitionExit;
191 private int mNextAppTransitionInPlace;
194 private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
195 = new SparseArray<>();
196 private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture;
197 private boolean mNextAppTransitionAnimationsSpecsPending;
198 private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
200 private Rect mNextAppTransitionInsets = new Rect();
202 private Rect mTmpFromClipRect = new Rect();
203 private Rect mTmpToClipRect = new Rect();
205 private final Rect mTmpRect = new Rect();
207 private final static int APP_STATE_IDLE = 0;
208 private final static int APP_STATE_READY = 1;
209 private final static int APP_STATE_RUNNING = 2;
210 private final static int APP_STATE_TIMEOUT = 3;
211 private int mAppTransitionState = APP_STATE_IDLE;
213 private final int mConfigShortAnimTime;
214 private final Interpolator mDecelerateInterpolator;
215 private final Interpolator mThumbnailFadeInInterpolator;
216 private final Interpolator mThumbnailFadeOutInterpolator;
217 private final Interpolator mLinearOutSlowInInterpolator;
218 private final Interpolator mFastOutLinearInInterpolator;
219 private final Interpolator mFastOutSlowInInterpolator;
220 private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
222 private final int mClipRevealTranslationY;
224 private int mCurrentUserId = 0;
225 private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
227 private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
228 private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
230 private int mLastClipRevealMaxTranslation;
231 private boolean mLastHadClipReveal;
232 private boolean mProlongedAnimationsEnded;
234 AppTransition(Context context, WindowManagerService service) {
237 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
238 com.android.internal.R.interpolator.linear_out_slow_in);
239 mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
240 com.android.internal.R.interpolator.fast_out_linear_in);
241 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
242 com.android.internal.R.interpolator.fast_out_slow_in);
243 mConfigShortAnimTime = context.getResources().getInteger(
244 com.android.internal.R.integer.config_shortAnimTime);
245 mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
246 com.android.internal.R.interpolator.decelerate_cubic);
247 mThumbnailFadeInInterpolator = new Interpolator() {
249 public float getInterpolation(float input) {
250 // Linear response for first fraction, then complete after that.
251 if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
254 float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
255 (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
256 return mFastOutLinearInInterpolator.getInterpolation(t);
259 mThumbnailFadeOutInterpolator = new Interpolator() {
261 public float getInterpolation(float input) {
262 // Linear response for first fraction, then complete after that.
263 if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
264 float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
265 return mLinearOutSlowInInterpolator.getInterpolation(t);
270 mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
271 * mContext.getResources().getDisplayMetrics().density);
274 boolean isTransitionSet() {
275 return mNextAppTransition != TRANSIT_UNSET;
278 boolean isTransitionEqual(int transit) {
279 return mNextAppTransition == transit;
282 int getAppTransition() {
283 return mNextAppTransition;
286 private void setAppTransition(int transit) {
287 mNextAppTransition = transit;
291 return mAppTransitionState == APP_STATE_READY
292 || mAppTransitionState == APP_STATE_TIMEOUT;
296 mAppTransitionState = APP_STATE_READY;
297 fetchAppTransitionSpecsFromFuture();
300 boolean isRunning() {
301 return mAppTransitionState == APP_STATE_RUNNING;
305 mAppTransitionState = APP_STATE_IDLE;
308 boolean isTimeout() {
309 return mAppTransitionState == APP_STATE_TIMEOUT;
313 mAppTransitionState = APP_STATE_TIMEOUT;
316 Bitmap getAppTransitionThumbnailHeader(int taskId) {
317 AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
319 spec = mDefaultNextAppTransitionAnimationSpec;
321 return spec != null ? spec.bitmap : null;
324 /** Returns whether the next thumbnail transition is aspect scaled up. */
325 boolean isNextThumbnailTransitionAspectScaled() {
326 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
327 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
330 /** Returns whether the next thumbnail transition is scaling up. */
331 boolean isNextThumbnailTransitionScaleUp() {
332 return mNextAppTransitionScaleUp;
335 boolean isNextAppTransitionThumbnailUp() {
336 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
337 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP;
340 boolean isNextAppTransitionThumbnailDown() {
341 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN ||
342 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
346 * @return true if and only if we are currently fetching app transition specs from the future
347 * passed into {@link #overridePendingAppTransitionMultiThumbFuture}
349 boolean isFetchingAppTransitionsSpecs() {
350 return mNextAppTransitionAnimationsSpecsPending;
353 private boolean prepare() {
355 mAppTransitionState = APP_STATE_IDLE;
356 notifyAppTransitionPendingLocked();
357 mLastHadClipReveal = false;
358 mLastClipRevealMaxTranslation = 0;
359 mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
365 void goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator,
366 ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps) {
367 mNextAppTransition = TRANSIT_UNSET;
368 mAppTransitionState = APP_STATE_RUNNING;
369 notifyAppTransitionStartingLocked(
370 topOpeningAppAnimator != null ? topOpeningAppAnimator.mAppToken.token : null,
371 topClosingAppAnimator != null ? topClosingAppAnimator.mAppToken.token : null,
372 topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null,
373 topClosingAppAnimator != null ? topClosingAppAnimator.animation : null);
374 mService.getDefaultDisplayContentLocked().getDockedDividerController()
375 .notifyAppTransitionStarting();
377 // Prolong the start for the transition when docking a task from recents, unless recents
378 // ended it already then we don't need to wait.
379 if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS && !mProlongedAnimationsEnded) {
380 for (int i = openingApps.size() - 1; i >= 0; i--) {
381 final AppWindowAnimator appAnimator = openingApps.valueAt(i).mAppAnimator;
382 appAnimator.startProlongAnimation(PROLONG_ANIMATION_AT_START);
388 * Let the transitions manager know that the somebody wanted to end the prolonged animations.
390 void notifyProlongedAnimationsEnded() {
391 mProlongedAnimationsEnded = true;
395 mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
396 mNextAppTransitionPackage = null;
397 mNextAppTransitionAnimationsSpecs.clear();
398 mNextAppTransitionAnimationsSpecsFuture = null;
399 mDefaultNextAppTransitionAnimationSpec = null;
400 mAnimationFinishedCallback = null;
401 mProlongedAnimationsEnded = false;
405 setAppTransition(AppTransition.TRANSIT_UNSET);
408 notifyAppTransitionCancelledLocked();
411 void registerListenerLocked(AppTransitionListener listener) {
412 mListeners.add(listener);
415 public void notifyAppTransitionFinishedLocked(IBinder token) {
416 for (int i = 0; i < mListeners.size(); i++) {
417 mListeners.get(i).onAppTransitionFinishedLocked(token);
421 private void notifyAppTransitionPendingLocked() {
422 for (int i = 0; i < mListeners.size(); i++) {
423 mListeners.get(i).onAppTransitionPendingLocked();
427 private void notifyAppTransitionCancelledLocked() {
428 for (int i = 0; i < mListeners.size(); i++) {
429 mListeners.get(i).onAppTransitionCancelledLocked();
433 private void notifyAppTransitionStartingLocked(IBinder openToken,
434 IBinder closeToken, Animation openAnimation, Animation closeAnimation) {
435 for (int i = 0; i < mListeners.size(); i++) {
436 mListeners.get(i).onAppTransitionStartingLocked(openToken, closeToken, openAnimation,
441 private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
442 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
443 + (lp != null ? lp.packageName : null)
444 + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
445 if (lp != null && lp.windowAnimations != 0) {
446 // If this is a system resource, don't try to load it from the
447 // application resources. It is nice to avoid loading application
448 // resources if we can.
449 String packageName = lp.packageName != null ? lp.packageName : "android";
450 int resId = lp.windowAnimations;
451 if ((resId&0xFF000000) == 0x01000000) {
452 packageName = "android";
454 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
456 return AttributeCache.instance().get(packageName, resId,
457 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
462 private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
463 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
464 + packageName + " resId=0x" + Integer.toHexString(resId));
465 if (packageName != null) {
466 if ((resId&0xFF000000) == 0x01000000) {
467 packageName = "android";
469 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
471 return AttributeCache.instance().get(packageName, resId,
472 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
477 Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
479 Context context = mContext;
481 AttributeCache.Entry ent = getCachedAnimations(lp);
483 context = ent.context;
484 anim = ent.array.getResourceId(animAttr, 0);
488 return AnimationUtils.loadAnimation(context, anim);
493 Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
494 Context context = mContext;
496 AttributeCache.Entry ent = getCachedAnimations(lp);
498 context = ent.context;
500 return AnimationUtils.loadAnimation(context, resId);
505 private Animation loadAnimationRes(String packageName, int resId) {
507 Context context = mContext;
509 AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
511 context = ent.context;
516 return AnimationUtils.loadAnimation(context, anim);
522 * Compute the pivot point for an animation that is scaling from a small
523 * rect on screen to a larger rect. The pivot point varies depending on
524 * the distance between the inner and outer edges on both sides. This
525 * function computes the pivot point for one dimension.
526 * @param startPos Offset from left/top edge of outer rectangle to
527 * left/top edge of inner rectangle.
528 * @param finalScale The scaling factor between the size of the outer
529 * and inner rectangles.
531 private static float computePivot(int startPos, float finalScale) {
534 Theorem of intercepting lines:
536 + + +-----------------------------------------------+
546 | + | +--------------------+ |
564 | | +--------------------+ |
572 | +-----------------------------------------------+
586 <=> x = -y / (scale - 1)
588 final float denom = finalScale-1;
589 if (Math.abs(denom) < .0001f) {
592 return -startPos / denom;
595 private Animation createScaleUpAnimationLocked(int transit, boolean enter,
596 Rect containingFrame) {
598 getDefaultNextAppTransitionStartRect(mTmpRect);
599 final int appWidth = containingFrame.width();
600 final int appHeight = containingFrame.height();
602 // Entering app zooms out from the center of the initial rect.
603 float scaleW = mTmpRect.width() / (float) appWidth;
604 float scaleH = mTmpRect.height() / (float) appHeight;
605 Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
606 computePivot(mTmpRect.left, scaleW),
607 computePivot(mTmpRect.right, scaleH));
608 scale.setInterpolator(mDecelerateInterpolator);
610 Animation alpha = new AlphaAnimation(0, 1);
611 alpha.setInterpolator(mThumbnailFadeOutInterpolator);
613 AnimationSet set = new AnimationSet(false);
614 set.addAnimation(scale);
615 set.addAnimation(alpha);
616 set.setDetachWallpaper(true);
618 } else if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
619 transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
620 // If we are on top of the wallpaper, we need an animation that
621 // correctly handles the wallpaper staying static behind all of
622 // the animated elements. To do this, will just have the existing
624 a = new AlphaAnimation(1, 0);
625 a.setDetachWallpaper(true);
627 // For normal animations, the exiting element just holds in place.
628 a = new AlphaAnimation(1, 1);
631 // Pick the desired duration. If this is an inter-activity transition,
632 // it is the standard duration for that. Otherwise we use the longer
633 // task transition duration.
636 case TRANSIT_ACTIVITY_OPEN:
637 case TRANSIT_ACTIVITY_CLOSE:
638 duration = mConfigShortAnimTime;
641 duration = DEFAULT_APP_TRANSITION_DURATION;
644 a.setDuration(duration);
645 a.setFillAfter(true);
646 a.setInterpolator(mDecelerateInterpolator);
647 a.initialize(appWidth, appHeight, appWidth, appHeight);
651 private void getDefaultNextAppTransitionStartRect(Rect rect) {
652 if (mDefaultNextAppTransitionAnimationSpec == null ||
653 mDefaultNextAppTransitionAnimationSpec.rect == null) {
654 Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
657 rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
661 void getNextAppTransitionStartRect(int taskId, Rect rect) {
662 AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
664 spec = mDefaultNextAppTransitionAnimationSpec;
666 if (spec == null || spec.rect == null) {
667 Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
675 private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
677 mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
678 bitmap, new Rect(left, top, left + width, top + height));
682 * @return the duration of the last clip reveal animation
684 long getLastClipRevealTransitionDuration() {
685 return mLastClipRevealTransitionDuration;
689 * @return the maximum distance the app surface is traveling of the last clip reveal animation
691 int getLastClipRevealMaxTranslation() {
692 return mLastClipRevealMaxTranslation;
696 * @return true if in the last app transition had a clip reveal animation, false otherwise
698 boolean hadClipRevealAnimation() {
699 return mLastHadClipReveal;
703 * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
704 * the start rect is outside of the target rect, and there is a lot of movement going on.
706 * @param cutOff whether the start rect was not fully contained by the end rect
707 * @param translationX the total translation the surface moves in x direction
708 * @param translationY the total translation the surfaces moves in y direction
709 * @param displayFrame our display frame
711 * @return the duration of the clip reveal animation, in milliseconds
713 private long calculateClipRevealTransitionDuration(boolean cutOff, float translationX,
714 float translationY, Rect displayFrame) {
716 return DEFAULT_APP_TRANSITION_DURATION;
718 final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(),
719 Math.abs(translationY) / displayFrame.height());
720 return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction *
721 (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
724 private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
726 final Animation anim;
728 final int appWidth = appFrame.width();
729 final int appHeight = appFrame.height();
731 // mTmpRect will contain an area around the launcher icon that was pressed. We will
732 // clip reveal from that area in the final area of the app.
733 getDefaultNextAppTransitionStartRect(mTmpRect);
737 t = (float) mTmpRect.top / displayFrame.height();
739 int translationY = mClipRevealTranslationY + (int)(displayFrame.height() / 7f * t);
740 int translationX = 0;
741 int translationYCorrection = translationY;
742 int centerX = mTmpRect.centerX();
743 int centerY = mTmpRect.centerY();
744 int halfWidth = mTmpRect.width() / 2;
745 int halfHeight = mTmpRect.height() / 2;
746 int clipStartX = centerX - halfWidth - appFrame.left;
747 int clipStartY = centerY - halfHeight - appFrame.top;
748 boolean cutOff = false;
750 // If the starting rectangle is fully or partially outside of the target rectangle, we
751 // need to start the clipping at the edge and then achieve the rest with translation
752 // and extending the clip rect from that edge.
753 if (appFrame.top > centerY - halfHeight) {
754 translationY = (centerY - halfHeight) - appFrame.top;
755 translationYCorrection = 0;
759 if (appFrame.left > centerX - halfWidth) {
760 translationX = (centerX - halfWidth) - appFrame.left;
764 if (appFrame.right < centerX + halfWidth) {
765 translationX = (centerX + halfWidth) - appFrame.right;
766 clipStartX = appWidth - mTmpRect.width();
769 final long duration = calculateClipRevealTransitionDuration(cutOff, translationX,
770 translationY, displayFrame);
772 // Clip third of the from size of launch icon, expand to full width/height
773 Animation clipAnimLR = new ClipRectLRAnimation(
774 clipStartX, clipStartX + mTmpRect.width(), 0, appWidth);
775 clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
776 clipAnimLR.setDuration((long) (duration / 2.5f));
778 TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0);
779 translate.setInterpolator(cutOff ? TOUCH_RESPONSE_INTERPOLATOR
780 : mLinearOutSlowInInterpolator);
781 translate.setDuration(duration);
783 Animation clipAnimTB = new ClipRectTBAnimation(
784 clipStartY, clipStartY + mTmpRect.height(),
786 translationYCorrection, 0,
787 mLinearOutSlowInInterpolator);
788 clipAnimTB.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
789 clipAnimTB.setDuration(duration);
791 // Quick fade-in from icon to app window
792 final long alphaDuration = duration / 4;
793 AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
794 alpha.setDuration(alphaDuration);
795 alpha.setInterpolator(mLinearOutSlowInInterpolator);
797 AnimationSet set = new AnimationSet(false);
798 set.addAnimation(clipAnimLR);
799 set.addAnimation(clipAnimTB);
800 set.addAnimation(translate);
801 set.addAnimation(alpha);
802 set.setZAdjustment(Animation.ZORDER_TOP);
803 set.initialize(appWidth, appHeight, appWidth, appHeight);
805 mLastHadClipReveal = true;
806 mLastClipRevealTransitionDuration = duration;
808 // If the start rect was full inside the target rect (cutOff == false), we don't need
809 // to store the translation, because it's only used if cutOff == true.
810 mLastClipRevealMaxTranslation = cutOff
811 ? Math.max(Math.abs(translationY), Math.abs(translationX)) : 0;
815 case TRANSIT_ACTIVITY_OPEN:
816 case TRANSIT_ACTIVITY_CLOSE:
817 duration = mConfigShortAnimTime;
820 duration = DEFAULT_APP_TRANSITION_DURATION;
823 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
824 transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
825 // If we are on top of the wallpaper, we need an animation that
826 // correctly handles the wallpaper staying static behind all of
827 // the animated elements. To do this, will just have the existing
829 anim = new AlphaAnimation(1, 0);
830 anim.setDetachWallpaper(true);
832 // For normal animations, the exiting element just holds in place.
833 anim = new AlphaAnimation(1, 1);
835 anim.setInterpolator(mDecelerateInterpolator);
836 anim.setDuration(duration);
837 anim.setFillAfter(true);
843 * Prepares the specified animation with a standard duration, interpolator, etc.
845 Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
846 long duration, Interpolator interpolator) {
848 a.setDuration(duration);
850 a.setFillAfter(true);
851 if (interpolator != null) {
852 a.setInterpolator(interpolator);
854 a.initialize(appWidth, appHeight, appWidth, appHeight);
859 * Prepares the specified animation with a standard duration, interpolator, etc.
861 Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
862 // Pick the desired duration. If this is an inter-activity transition,
863 // it is the standard duration for that. Otherwise we use the longer
864 // task transition duration.
867 case TRANSIT_ACTIVITY_OPEN:
868 case TRANSIT_ACTIVITY_CLOSE:
869 duration = mConfigShortAnimTime;
872 duration = DEFAULT_APP_TRANSITION_DURATION;
875 return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
876 mDecelerateInterpolator);
880 * Return the current thumbnail transition state.
882 int getThumbnailTransitionState(boolean enter) {
884 if (mNextAppTransitionScaleUp) {
885 return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
887 return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
890 if (mNextAppTransitionScaleUp) {
891 return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
893 return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
899 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
900 * when a thumbnail is specified with the pending animation override.
902 Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
903 Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation) {
905 final int thumbWidthI = thumbnailHeader.getWidth();
906 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
907 final int thumbHeightI = thumbnailHeader.getHeight();
908 final int appWidth = appRect.width();
910 float scaleW = appWidth / thumbWidth;
911 getNextAppTransitionStartRect(taskId, mTmpRect);
918 if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
919 fromX = mTmpRect.left;
920 fromY = mTmpRect.top;
922 // For the curved translate animation to work, the pivot points needs to be at the
923 // same absolute position as the one from the real surface.
924 toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
925 toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
926 pivotX = mTmpRect.width() / 2;
927 pivotY = appRect.height() / 2 / scaleW;
931 fromX = mTmpRect.left;
932 fromY = mTmpRect.top;
936 final long duration = getAspectScaleDuration();
937 final Interpolator interpolator = getAspectScaleInterpolator();
938 if (mNextAppTransitionScaleUp) {
939 // Animation up from the thumbnail to the full screen
940 Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
941 scale.setInterpolator(interpolator);
942 scale.setDuration(duration);
943 Animation alpha = new AlphaAnimation(1f, 0f);
944 alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
945 ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
946 alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
949 Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
950 translate.setInterpolator(interpolator);
951 translate.setDuration(duration);
953 mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
954 mTmpToClipRect.set(appRect);
956 // Containing frame is in screen space, but we need the clip rect in the
958 mTmpToClipRect.offsetTo(0, 0);
959 mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
960 mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
962 if (contentInsets != null) {
963 mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
964 (int) (-contentInsets.top * scaleW),
965 (int) (-contentInsets.right * scaleW),
966 (int) (-contentInsets.bottom * scaleW));
969 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
970 clipAnim.setInterpolator(interpolator);
971 clipAnim.setDuration(duration);
973 // This AnimationSet uses the Interpolators assigned above.
974 AnimationSet set = new AnimationSet(false);
975 set.addAnimation(scale);
976 set.addAnimation(alpha);
977 set.addAnimation(translate);
978 set.addAnimation(clipAnim);
981 // Animation down from the full screen to the thumbnail
982 Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
983 scale.setInterpolator(interpolator);
984 scale.setDuration(duration);
985 Animation alpha = new AlphaAnimation(0f, 1f);
986 alpha.setInterpolator(mThumbnailFadeInInterpolator);
987 alpha.setDuration(duration);
988 Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
989 translate.setInterpolator(interpolator);
990 translate.setDuration(duration);
992 // This AnimationSet uses the Interpolators assigned above.
993 AnimationSet set = new AnimationSet(false);
994 set.addAnimation(scale);
995 set.addAnimation(alpha);
996 set.addAnimation(translate);
1000 return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
1004 private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
1006 // Almost no x-change - use linear animation
1007 if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
1008 return new TranslateAnimation(fromX, toX, fromY, toY);
1010 final Path path = createCurvedPath(fromX, toX, fromY, toY);
1011 return new CurvedTranslateAnimation(path);
1015 private Path createCurvedPath(float fromX, float toX, float fromY, float toY) {
1016 final Path path = new Path();
1017 path.moveTo(fromX, fromY);
1020 // If the object needs to go up, move it in horizontal direction first, then vertical.
1021 path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY);
1023 // If the object needs to go down, move it in vertical direction first, then horizontal.
1024 path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY);
1029 private long getAspectScaleDuration() {
1030 if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
1031 return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
1033 return THUMBNAIL_APP_TRANSITION_DURATION;
1037 private Interpolator getAspectScaleInterpolator() {
1038 if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
1039 return mFastOutSlowInInterpolator;
1041 return TOUCH_RESPONSE_INTERPOLATOR;
1046 * This alternate animation is created when we are doing a thumbnail transition, for the
1047 * activity that is leaving, and the activity that is entering.
1049 Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
1050 int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
1051 @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
1053 final int appWidth = containingFrame.width();
1054 final int appHeight = containingFrame.height();
1055 getDefaultNextAppTransitionStartRect(mTmpRect);
1056 final int thumbWidthI = mTmpRect.width();
1057 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1058 final int thumbHeightI = mTmpRect.height();
1059 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1060 final int thumbStartX = mTmpRect.left - containingFrame.left;
1061 final int thumbStartY = mTmpRect.top - containingFrame.top;
1063 switch (thumbTransitState) {
1064 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
1065 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1066 final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
1067 if (freeform && scaleUp) {
1068 a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
1069 containingFrame, surfaceInsets, taskId);
1070 } else if (freeform) {
1071 a = createAspectScaledThumbnailExitFreeformAnimationLocked(
1072 containingFrame, surfaceInsets, taskId);
1074 AnimationSet set = new AnimationSet(true);
1076 // In portrait, we scale to fit the width
1077 mTmpFromClipRect.set(containingFrame);
1078 mTmpToClipRect.set(containingFrame);
1080 // Containing frame is in screen space, but we need the clip rect in the
1082 mTmpFromClipRect.offsetTo(0, 0);
1083 mTmpToClipRect.offsetTo(0, 0);
1085 // Exclude insets region from the source clip.
1086 mTmpFromClipRect.inset(contentInsets);
1087 mNextAppTransitionInsets.set(contentInsets);
1089 if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
1090 // We scale the width and clip to the top/left square
1091 float scale = thumbWidth /
1092 (appWidth - contentInsets.left - contentInsets.right);
1093 int unscaledThumbHeight = (int) (thumbHeight / scale);
1094 mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
1096 mNextAppTransitionInsets.set(contentInsets);
1098 Animation scaleAnim = new ScaleAnimation(
1099 scaleUp ? scale : 1, scaleUp ? 1 : scale,
1100 scaleUp ? scale : 1, scaleUp ? 1 : scale,
1101 containingFrame.width() / 2f,
1102 containingFrame.height() / 2f + contentInsets.top);
1103 final float targetX = (mTmpRect.left - containingFrame.left);
1104 final float x = containingFrame.width() / 2f
1105 - containingFrame.width() / 2f * scale;
1106 final float targetY = (mTmpRect.top - containingFrame.top);
1107 final float y = containingFrame.height() / 2f
1108 - containingFrame.height() / 2f * scale;
1109 final float startX = targetX - x;
1110 final float startY = targetY - y;
1111 Animation clipAnim = scaleUp
1112 ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1113 : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1114 Animation translateAnim = scaleUp
1115 ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
1116 : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
1118 set.addAnimation(clipAnim);
1119 set.addAnimation(scaleAnim);
1120 set.addAnimation(translateAnim);
1123 // In landscape, we don't scale at all and only crop
1124 mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
1125 mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
1127 Animation clipAnim = scaleUp
1128 ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1129 : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1130 Animation translateAnim = scaleUp
1131 ? createCurvedMotion(thumbStartX, 0,
1132 thumbStartY - contentInsets.top, 0)
1133 : createCurvedMotion(0, thumbStartX, 0,
1134 thumbStartY - contentInsets.top);
1136 set.addAnimation(clipAnim);
1137 set.addAnimation(translateAnim);
1140 a.setZAdjustment(Animation.ZORDER_TOP);
1144 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1145 // Previous app window during the scale up
1146 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1147 // Fade out the source activity if we are animating to a wallpaper
1149 a = new AlphaAnimation(1, 0);
1151 a = new AlphaAnimation(1, 1);
1155 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1156 // Target app window during the scale down
1157 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1158 // Fade in the destination activity if we are animating from a wallpaper
1160 a = new AlphaAnimation(0, 1);
1162 a = new AlphaAnimation(1, 1);
1167 throw new RuntimeException("Invalid thumbnail transition state");
1170 return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
1171 getAspectScaleDuration(), getAspectScaleInterpolator());
1174 private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
1175 @Nullable Rect surfaceInsets, int taskId) {
1176 getNextAppTransitionStartRect(taskId, mTmpRect);
1177 return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
1181 private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
1182 @Nullable Rect surfaceInsets, int taskId) {
1183 getNextAppTransitionStartRect(taskId, mTmpRect);
1184 return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
1188 private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
1189 Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
1190 final float sourceWidth = sourceFrame.width();
1191 final float sourceHeight = sourceFrame.height();
1192 final float destWidth = destFrame.width();
1193 final float destHeight = destFrame.height();
1194 final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
1195 final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
1196 AnimationSet set = new AnimationSet(true);
1197 final int surfaceInsetsH = surfaceInsets == null
1198 ? 0 : surfaceInsets.left + surfaceInsets.right;
1199 final int surfaceInsetsV = surfaceInsets == null
1200 ? 0 : surfaceInsets.top + surfaceInsets.bottom;
1201 // We want the scaling to happen from the center of the surface. In order to achieve that,
1202 // we need to account for surface insets that will be used to enlarge the surface.
1203 final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
1204 final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
1205 final ScaleAnimation scale = enter ?
1206 new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
1207 : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
1208 final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
1209 final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
1210 final int destHCenter = destFrame.left + destFrame.width() / 2;
1211 final int destVCenter = destFrame.top + destFrame.height() / 2;
1212 final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
1213 final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
1214 final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
1215 : new TranslateAnimation(0, fromX, 0, fromY);
1216 set.addAnimation(scale);
1217 set.addAnimation(translation);
1219 final IRemoteCallback callback = mAnimationFinishedCallback;
1220 if (callback != null) {
1221 set.setAnimationListener(new Animation.AnimationListener() {
1223 public void onAnimationStart(Animation animation) { }
1226 public void onAnimationEnd(Animation animation) {
1227 mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
1231 public void onAnimationRepeat(Animation animation) { }
1238 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
1239 * when a thumbnail is specified with the pending animation override.
1241 Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
1242 Bitmap thumbnailHeader) {
1244 getDefaultNextAppTransitionStartRect(mTmpRect);
1245 final int thumbWidthI = thumbnailHeader.getWidth();
1246 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1247 final int thumbHeightI = thumbnailHeader.getHeight();
1248 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1250 if (mNextAppTransitionScaleUp) {
1251 // Animation for the thumbnail zooming from its initial size to the full screen
1252 float scaleW = appWidth / thumbWidth;
1253 float scaleH = appHeight / thumbHeight;
1254 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1255 computePivot(mTmpRect.left, 1 / scaleW),
1256 computePivot(mTmpRect.top, 1 / scaleH));
1257 scale.setInterpolator(mDecelerateInterpolator);
1259 Animation alpha = new AlphaAnimation(1, 0);
1260 alpha.setInterpolator(mThumbnailFadeOutInterpolator);
1262 // This AnimationSet uses the Interpolators assigned above.
1263 AnimationSet set = new AnimationSet(false);
1264 set.addAnimation(scale);
1265 set.addAnimation(alpha);
1268 // Animation for the thumbnail zooming down from the full screen to its final size
1269 float scaleW = appWidth / thumbWidth;
1270 float scaleH = appHeight / thumbHeight;
1271 a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1272 computePivot(mTmpRect.left, 1 / scaleW),
1273 computePivot(mTmpRect.top, 1 / scaleH));
1276 return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1280 * This animation is created when we are doing a thumbnail transition, for the activity that is
1281 * leaving, and the activity that is entering.
1283 Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
1284 int transit, int taskId) {
1285 final int appWidth = containingFrame.width();
1286 final int appHeight = containingFrame.height();
1287 Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
1289 getDefaultNextAppTransitionStartRect(mTmpRect);
1290 final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
1291 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1292 final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
1293 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1295 switch (thumbTransitState) {
1296 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
1297 // Entering app scales up with the thumbnail
1298 float scaleW = thumbWidth / appWidth;
1299 float scaleH = thumbHeight / appHeight;
1300 a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1301 computePivot(mTmpRect.left, scaleW),
1302 computePivot(mTmpRect.top, scaleH));
1305 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1306 // Exiting app while the thumbnail is scaling up should fade or stay in place
1307 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1308 // Fade out while bringing up selected activity. This keeps the
1309 // current activity from showing through a launching wallpaper
1311 a = new AlphaAnimation(1, 0);
1314 a = new AlphaAnimation(1, 1);
1318 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1319 // Entering the other app, it should just be visible while we scale the thumbnail
1321 a = new AlphaAnimation(1, 1);
1324 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1325 // Exiting the current app, the app should scale down with the thumbnail
1326 float scaleW = thumbWidth / appWidth;
1327 float scaleH = thumbHeight / appHeight;
1328 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1329 computePivot(mTmpRect.left, scaleW),
1330 computePivot(mTmpRect.top, scaleH));
1332 Animation alpha = new AlphaAnimation(1, 0);
1334 AnimationSet set = new AnimationSet(true);
1335 set.addAnimation(scale);
1336 set.addAnimation(alpha);
1337 set.setZAdjustment(Animation.ZORDER_TOP);
1342 throw new RuntimeException("Invalid thumbnail transition state");
1345 return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1348 private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
1349 getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
1350 final int left = mTmpFromClipRect.left;
1351 final int top = mTmpFromClipRect.top;
1352 mTmpFromClipRect.offset(-left, -top);
1353 // TODO: Isn't that strange that we ignore exact position of the containingFrame?
1354 mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
1355 AnimationSet set = new AnimationSet(true);
1356 float fromWidth = mTmpFromClipRect.width();
1357 float toWidth = mTmpToClipRect.width();
1358 float fromHeight = mTmpFromClipRect.height();
1359 // While the window might span the whole display, the actual content will be cropped to the
1360 // system decoration frame, for example when the window is docked. We need to take into
1361 // account the visible height when constructing the animation.
1362 float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
1363 int translateAdjustment = 0;
1364 if (fromWidth <= toWidth && fromHeight <= toHeight) {
1365 // The final window is larger in both dimensions than current window (e.g. we are
1366 // maximizing), so we can simply unclip the new window and there will be no disappearing
1368 set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
1370 // The disappearing window has one larger dimension. We need to apply scaling, so the
1371 // first frame of the entry animation matches the old window.
1372 set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
1373 // We might not be going exactly full screen, but instead be aligned under the status
1374 // bar using cropping. We still need to account for the cropped part, which will also
1376 translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
1379 // We animate the translation from the old position of the removed window, to the new
1380 // position of the added window. The latter might not be full screen, for example docked for
1382 TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
1383 0, top - containingFrame.top - translateAdjustment, 0);
1384 set.addAnimation(translate);
1385 set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
1386 set.setZAdjustment(Animation.ZORDER_TOP);
1391 * @return true if and only if the first frame of the transition can be skipped, i.e. the first
1392 * frame of the transition doesn't change the visuals on screen, so we can start
1393 * directly with the second one
1395 boolean canSkipFirstFrame() {
1396 return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
1397 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
1398 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1403 * @param frame These are the bounds of the window when it finishes the animation. This is where
1404 * the animation must usually finish in entrance animation, as the next frame will
1405 * display the window at these coordinates. In case of exit animation, this is
1406 * where the animation must start, as the frame before the animation is displaying
1407 * the window at these bounds.
1408 * @param insets Knowing where the window will be positioned is not enough. Some parts of the
1409 * window might be obscured, usually by the system windows (status bar and
1410 * navigation bar) and we use content insets to convey that information. This
1411 * usually affects the animation aspects vertically, as the system decoration is
1412 * at the top and the bottom. For example when we animate from full screen to
1413 * recents, we want to exclude the covered parts, because they won't match the
1414 * thumbnail after the last frame is executed.
1415 * @param surfaceInsets In rare situation the surface is larger than the content and we need to
1416 * know about this to make the animation frames match. We currently use
1417 * this for freeform windows, which have larger surfaces to display
1418 * shadows. When we animate them from recents, we want to match the content
1419 * to the recents thumbnail and hence need to account for the surface being
1422 Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
1423 int orientation, Rect frame, Rect displayFrame, Rect insets,
1424 @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
1427 if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
1428 || transit == TRANSIT_TASK_OPEN
1429 || transit == TRANSIT_TASK_TO_FRONT)) {
1430 a = loadAnimationRes(lp, enter
1431 ? com.android.internal.R.anim.voice_activity_open_enter
1432 : com.android.internal.R.anim.voice_activity_open_exit);
1433 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1434 "applyAnimation voice:"
1435 + " anim=" + a + " transit=" + appTransitionToString(transit)
1436 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1437 } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
1438 || transit == TRANSIT_TASK_CLOSE
1439 || transit == TRANSIT_TASK_TO_BACK)) {
1440 a = loadAnimationRes(lp, enter
1441 ? com.android.internal.R.anim.voice_activity_close_enter
1442 : com.android.internal.R.anim.voice_activity_close_exit);
1443 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1444 "applyAnimation voice:"
1445 + " anim=" + a + " transit=" + appTransitionToString(transit)
1446 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1447 } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
1448 a = createRelaunchAnimation(frame, insets);
1449 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1451 + " anim=" + a + " nextAppTransition=" + mNextAppTransition
1452 + " transit=" + appTransitionToString(transit)
1453 + " Callers=" + Debug.getCallers(3));
1454 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
1455 a = loadAnimationRes(mNextAppTransitionPackage, enter ?
1456 mNextAppTransitionEnter : mNextAppTransitionExit);
1457 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1459 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
1460 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1461 + " Callers=" + Debug.getCallers(3));
1462 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
1463 a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
1464 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1466 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
1467 + " transit=" + appTransitionToString(transit)
1468 + " Callers=" + Debug.getCallers(3));
1469 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
1470 a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
1471 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1473 + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
1474 + " transit=" + appTransitionToString(transit)
1475 + " Callers=" + Debug.getCallers(3));
1476 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
1477 a = createScaleUpAnimationLocked(transit, enter, frame);
1478 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1480 + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
1481 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1482 + " Callers=" + Debug.getCallers(3));
1483 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
1484 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
1485 mNextAppTransitionScaleUp =
1486 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
1487 a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
1488 frame, transit, taskId);
1489 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1490 String animName = mNextAppTransitionScaleUp ?
1491 "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
1492 Slog.v(TAG, "applyAnimation:"
1493 + " anim=" + a + " nextAppTransition=" + animName
1494 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1495 + " Callers=" + Debug.getCallers(3));
1497 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
1498 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
1499 mNextAppTransitionScaleUp =
1500 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
1501 a = createAspectScaledThumbnailEnterExitAnimationLocked(
1502 getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
1503 insets, surfaceInsets, freeform, taskId);
1504 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1505 String animName = mNextAppTransitionScaleUp ?
1506 "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
1507 Slog.v(TAG, "applyAnimation:"
1508 + " anim=" + a + " nextAppTransition=" + animName
1509 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1510 + " Callers=" + Debug.getCallers(3));
1515 case TRANSIT_ACTIVITY_OPEN:
1517 ? WindowAnimation_activityOpenEnterAnimation
1518 : WindowAnimation_activityOpenExitAnimation;
1520 case TRANSIT_ACTIVITY_CLOSE:
1522 ? WindowAnimation_activityCloseEnterAnimation
1523 : WindowAnimation_activityCloseExitAnimation;
1525 case TRANSIT_DOCK_TASK_FROM_RECENTS:
1526 case TRANSIT_TASK_OPEN:
1528 ? WindowAnimation_taskOpenEnterAnimation
1529 : WindowAnimation_taskOpenExitAnimation;
1531 case TRANSIT_TASK_CLOSE:
1533 ? WindowAnimation_taskCloseEnterAnimation
1534 : WindowAnimation_taskCloseExitAnimation;
1536 case TRANSIT_TASK_TO_FRONT:
1538 ? WindowAnimation_taskToFrontEnterAnimation
1539 : WindowAnimation_taskToFrontExitAnimation;
1541 case TRANSIT_TASK_TO_BACK:
1543 ? WindowAnimation_taskToBackEnterAnimation
1544 : WindowAnimation_taskToBackExitAnimation;
1546 case TRANSIT_WALLPAPER_OPEN:
1548 ? WindowAnimation_wallpaperOpenEnterAnimation
1549 : WindowAnimation_wallpaperOpenExitAnimation;
1551 case TRANSIT_WALLPAPER_CLOSE:
1553 ? WindowAnimation_wallpaperCloseEnterAnimation
1554 : WindowAnimation_wallpaperCloseExitAnimation;
1556 case TRANSIT_WALLPAPER_INTRA_OPEN:
1558 ? WindowAnimation_wallpaperIntraOpenEnterAnimation
1559 : WindowAnimation_wallpaperIntraOpenExitAnimation;
1561 case TRANSIT_WALLPAPER_INTRA_CLOSE:
1563 ? WindowAnimation_wallpaperIntraCloseEnterAnimation
1564 : WindowAnimation_wallpaperIntraCloseExitAnimation;
1566 case TRANSIT_TASK_OPEN_BEHIND:
1568 ? WindowAnimation_launchTaskBehindSourceAnimation
1569 : WindowAnimation_launchTaskBehindTargetAnimation;
1571 a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
1572 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1575 + " animAttr=0x" + Integer.toHexString(animAttr)
1576 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1577 + " Callers=" + Debug.getCallers(3));
1582 int getAppStackClipMode() {
1583 return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
1584 || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
1585 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
1587 : STACK_CLIP_AFTER_ANIM;
1590 void postAnimationCallback() {
1591 if (mNextAppTransitionCallback != null) {
1592 mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
1593 mNextAppTransitionCallback));
1594 mNextAppTransitionCallback = null;
1598 void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
1599 IRemoteCallback startedCallback) {
1600 if (isTransitionSet()) {
1602 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
1603 mNextAppTransitionPackage = packageName;
1604 mNextAppTransitionEnter = enterAnim;
1605 mNextAppTransitionExit = exitAnim;
1606 postAnimationCallback();
1607 mNextAppTransitionCallback = startedCallback;
1609 postAnimationCallback();
1613 void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
1615 if (isTransitionSet()) {
1617 mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
1618 putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
1619 startY + startHeight, null);
1620 postAnimationCallback();
1624 void overridePendingAppTransitionClipReveal(int startX, int startY,
1625 int startWidth, int startHeight) {
1626 if (isTransitionSet()) {
1628 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1629 putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
1630 postAnimationCallback();
1634 void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
1635 IRemoteCallback startedCallback, boolean scaleUp) {
1636 if (isTransitionSet()) {
1638 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
1639 : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
1640 mNextAppTransitionScaleUp = scaleUp;
1641 putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
1642 postAnimationCallback();
1643 mNextAppTransitionCallback = startedCallback;
1645 postAnimationCallback();
1649 void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
1650 int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
1651 if (isTransitionSet()) {
1653 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1654 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1655 mNextAppTransitionScaleUp = scaleUp;
1656 putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
1658 postAnimationCallback();
1659 mNextAppTransitionCallback = startedCallback;
1661 postAnimationCallback();
1665 public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
1666 IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
1668 if (isTransitionSet()) {
1670 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1671 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1672 mNextAppTransitionScaleUp = scaleUp;
1673 if (specs != null) {
1674 for (int i = 0; i < specs.length; i++) {
1675 AppTransitionAnimationSpec spec = specs[i];
1677 mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
1679 // In full screen mode, the transition code depends on the default spec
1681 Rect rect = spec.rect;
1682 putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
1683 rect.width(), rect.height(), spec.bitmap);
1688 postAnimationCallback();
1689 mNextAppTransitionCallback = onAnimationStartedCallback;
1690 mAnimationFinishedCallback = onAnimationFinishedCallback;
1692 postAnimationCallback();
1696 void overridePendingAppTransitionMultiThumbFuture(
1697 IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
1699 if (isTransitionSet()) {
1701 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1702 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1703 mNextAppTransitionAnimationsSpecsFuture = specsFuture;
1704 mNextAppTransitionScaleUp = scaleUp;
1705 mNextAppTransitionFutureCallback = callback;
1709 void overrideInPlaceAppTransition(String packageName, int anim) {
1710 if (isTransitionSet()) {
1712 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
1713 mNextAppTransitionPackage = packageName;
1714 mNextAppTransitionInPlace = anim;
1716 postAnimationCallback();
1721 * If a future is set for the app transition specs, fetch it in another thread.
1723 private void fetchAppTransitionSpecsFromFuture() {
1724 if (mNextAppTransitionAnimationsSpecsFuture != null) {
1725 mNextAppTransitionAnimationsSpecsPending = true;
1726 final IAppTransitionAnimationSpecsFuture future
1727 = mNextAppTransitionAnimationsSpecsFuture;
1728 mNextAppTransitionAnimationsSpecsFuture = null;
1729 mDefaultExecutor.execute(new Runnable() {
1732 AppTransitionAnimationSpec[] specs = null;
1734 specs = future.get();
1735 } catch (RemoteException e) {
1736 Slog.w(TAG, "Failed to fetch app transition specs: " + e);
1738 synchronized (mService.mWindowMap) {
1739 mNextAppTransitionAnimationsSpecsPending = false;
1740 overridePendingAppTransitionMultiThumb(specs,
1741 mNextAppTransitionFutureCallback, null /* finishedCallback */,
1742 mNextAppTransitionScaleUp);
1743 mNextAppTransitionFutureCallback = null;
1744 if (specs != null) {
1745 mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
1748 mService.requestTraversal();
1755 public String toString() {
1756 return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
1760 * Returns the human readable name of a window transition.
1762 * @param transition The window transition.
1763 * @return The transition symbolic name.
1765 public static String appTransitionToString(int transition) {
1766 switch (transition) {
1767 case TRANSIT_UNSET: {
1768 return "TRANSIT_UNSET";
1770 case TRANSIT_NONE: {
1771 return "TRANSIT_NONE";
1773 case TRANSIT_ACTIVITY_OPEN: {
1774 return "TRANSIT_ACTIVITY_OPEN";
1776 case TRANSIT_ACTIVITY_CLOSE: {
1777 return "TRANSIT_ACTIVITY_CLOSE";
1779 case TRANSIT_TASK_OPEN: {
1780 return "TRANSIT_TASK_OPEN";
1782 case TRANSIT_TASK_CLOSE: {
1783 return "TRANSIT_TASK_CLOSE";
1785 case TRANSIT_TASK_TO_FRONT: {
1786 return "TRANSIT_TASK_TO_FRONT";
1788 case TRANSIT_TASK_TO_BACK: {
1789 return "TRANSIT_TASK_TO_BACK";
1791 case TRANSIT_WALLPAPER_CLOSE: {
1792 return "TRANSIT_WALLPAPER_CLOSE";
1794 case TRANSIT_WALLPAPER_OPEN: {
1795 return "TRANSIT_WALLPAPER_OPEN";
1797 case TRANSIT_WALLPAPER_INTRA_OPEN: {
1798 return "TRANSIT_WALLPAPER_INTRA_OPEN";
1800 case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1801 return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1803 case TRANSIT_TASK_OPEN_BEHIND: {
1804 return "TRANSIT_TASK_OPEN_BEHIND";
1806 case TRANSIT_ACTIVITY_RELAUNCH: {
1807 return "TRANSIT_ACTIVITY_RELAUNCH";
1809 case TRANSIT_DOCK_TASK_FROM_RECENTS: {
1810 return "TRANSIT_DOCK_TASK_FROM_RECENTS";
1818 private String appStateToString() {
1819 switch (mAppTransitionState) {
1820 case APP_STATE_IDLE:
1821 return "APP_STATE_IDLE";
1822 case APP_STATE_READY:
1823 return "APP_STATE_READY";
1824 case APP_STATE_RUNNING:
1825 return "APP_STATE_RUNNING";
1826 case APP_STATE_TIMEOUT:
1827 return "APP_STATE_TIMEOUT";
1829 return "unknown state=" + mAppTransitionState;
1833 private String transitTypeToString() {
1834 switch (mNextAppTransitionType) {
1835 case NEXT_TRANSIT_TYPE_NONE:
1836 return "NEXT_TRANSIT_TYPE_NONE";
1837 case NEXT_TRANSIT_TYPE_CUSTOM:
1838 return "NEXT_TRANSIT_TYPE_CUSTOM";
1839 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1840 return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE";
1841 case NEXT_TRANSIT_TYPE_SCALE_UP:
1842 return "NEXT_TRANSIT_TYPE_SCALE_UP";
1843 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1844 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1845 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1846 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1847 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1848 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1849 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1850 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1852 return "unknown type=" + mNextAppTransitionType;
1857 public void dump(PrintWriter pw, String prefix) {
1858 pw.print(prefix); pw.println(this);
1859 pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString());
1860 if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1861 pw.print(prefix); pw.print("mNextAppTransitionType=");
1862 pw.println(transitTypeToString());
1864 switch (mNextAppTransitionType) {
1865 case NEXT_TRANSIT_TYPE_CUSTOM:
1866 pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1867 pw.println(mNextAppTransitionPackage);
1868 pw.print(prefix); pw.print("mNextAppTransitionEnter=0x");
1869 pw.print(Integer.toHexString(mNextAppTransitionEnter));
1870 pw.print(" mNextAppTransitionExit=0x");
1871 pw.println(Integer.toHexString(mNextAppTransitionExit));
1873 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1874 pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1875 pw.println(mNextAppTransitionPackage);
1876 pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
1877 pw.print(Integer.toHexString(mNextAppTransitionInPlace));
1879 case NEXT_TRANSIT_TYPE_SCALE_UP: {
1880 getDefaultNextAppTransitionStartRect(mTmpRect);
1881 pw.print(prefix); pw.print("mNextAppTransitionStartX=");
1882 pw.print(mTmpRect.left);
1883 pw.print(" mNextAppTransitionStartY=");
1884 pw.println(mTmpRect.top);
1885 pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
1886 pw.print(mTmpRect.width());
1887 pw.print(" mNextAppTransitionStartHeight=");
1888 pw.println(mTmpRect.height());
1891 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1892 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1893 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1894 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
1895 pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
1896 pw.println(mDefaultNextAppTransitionAnimationSpec);
1897 pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
1898 pw.println(mNextAppTransitionAnimationsSpecs);
1899 pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
1900 pw.println(mNextAppTransitionScaleUp);
1904 if (mNextAppTransitionCallback != null) {
1905 pw.print(prefix); pw.print("mNextAppTransitionCallback=");
1906 pw.println(mNextAppTransitionCallback);
1910 public void setCurrentUser(int newUserId) {
1911 mCurrentUserId = newUserId;
1915 * @return true if transition is not running and should not be skipped, false if transition is
1918 boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
1919 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
1920 + " transit=" + appTransitionToString(transit)
1922 + " alwaysKeepCurrent=" + alwaysKeepCurrent
1923 + " Callers=" + Debug.getCallers(3));
1924 if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
1925 setAppTransition(transit);
1926 } else if (!alwaysKeepCurrent) {
1927 if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
1928 // Opening a new task always supersedes a close for the anim.
1929 setAppTransition(transit);
1930 } else if (transit == TRANSIT_ACTIVITY_OPEN
1931 && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
1932 // Opening a new activity always supersedes a close for the anim.
1933 setAppTransition(transit);
1936 boolean prepared = prepare();
1937 if (isTransitionSet()) {
1938 mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
1939 mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
1945 * @return whether the specified {@param uiMode} is the TV mode.
1947 private boolean isTvUiMode(int uiMode) {
1948 return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0;