AppTransition.java revision 1a5203dfd5264104db018b8a09d50075b1af9b2d
1/*
2 * Copyright (C) 2011 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
19import static android.view.WindowManagerInternal.AppTransitionListener;
20import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
21import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
22import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
23import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
24import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
25import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
26import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
27import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
28import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
29import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
30import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation;
31import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
32import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
33import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
34import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation;
35import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
36import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation;
37import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
38import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation;
39import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
40import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
41import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
42
43import android.annotation.Nullable;
44import android.content.Context;
45import android.content.res.Configuration;
46import android.graphics.Bitmap;
47import android.graphics.Rect;
48import android.os.Debug;
49import android.os.Handler;
50import android.os.IBinder;
51import android.os.IRemoteCallback;
52import android.util.Slog;
53import android.util.SparseArray;
54import android.view.AppTransitionAnimationSpec;
55import android.view.WindowManager;
56import android.view.animation.AlphaAnimation;
57import android.view.animation.Animation;
58import android.view.animation.AnimationSet;
59import android.view.animation.AnimationUtils;
60import android.view.animation.ClipRectAnimation;
61import android.view.animation.ClipRectLRAnimation;
62import android.view.animation.ClipRectTBAnimation;
63import android.view.animation.Interpolator;
64import android.view.animation.PathInterpolator;
65import android.view.animation.ScaleAnimation;
66import android.view.animation.TranslateAnimation;
67
68import com.android.internal.util.DumpUtils.Dump;
69import com.android.server.AttributeCache;
70import com.android.server.wm.WindowManagerService.H;
71
72import java.io.PrintWriter;
73import java.util.ArrayList;
74
75// State management of app transitions.  When we are preparing for a
76// transition, mNextAppTransition will be the kind of transition to
77// perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
78// mOpeningApps and mClosingApps are the lists of tokens that will be
79// made visible or hidden at the next transition.
80public class AppTransition implements Dump {
81    private static final String TAG = "AppTransition";
82    private static final boolean DEBUG_APP_TRANSITIONS =
83            WindowManagerService.DEBUG_APP_TRANSITIONS;
84    private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
85    private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
86
87    /** Not set up for a transition. */
88    public static final int TRANSIT_UNSET = -1;
89    /** No animation for transition. */
90    public static final int TRANSIT_NONE = 0;
91    /** A window in a new activity is being opened on top of an existing one in the same task. */
92    public static final int TRANSIT_ACTIVITY_OPEN = 6;
93    /** The window in the top-most activity is being closed to reveal the
94     * previous activity in the same task. */
95    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
96    /** A window in a new task is being opened on top of an existing one
97     * in another activity's task. */
98    public static final int TRANSIT_TASK_OPEN = 8;
99    /** A window in the top-most activity is being closed to reveal the
100     * previous activity in a different task. */
101    public static final int TRANSIT_TASK_CLOSE = 9;
102    /** A window in an existing task is being displayed on top of an existing one
103     * in another activity's task. */
104    public static final int TRANSIT_TASK_TO_FRONT = 10;
105    /** A window in an existing task is being put below all other tasks. */
106    public static final int TRANSIT_TASK_TO_BACK = 11;
107    /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
108     * does, effectively closing the wallpaper. */
109    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
110    /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
111     * effectively opening the wallpaper. */
112    public static final int TRANSIT_WALLPAPER_OPEN = 13;
113    /** A window in a new activity is being opened on top of an existing one, and both are on top
114     * of the wallpaper. */
115    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
116    /** The window in the top-most activity is being closed to reveal the previous activity, and
117     * both are on top of the wallpaper. */
118    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
119    /** A window in a new task is being opened behind an existing one in another activity's task.
120     * The new window will show briefly and then be gone. */
121    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
122    /** A window in a task is being animated in-place. */
123    public static final int TRANSIT_TASK_IN_PLACE = 17;
124    /** An activity is being relaunched (e.g. due to configuration change). */
125    public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
126
127    /** Fraction of animation at which the recents thumbnail stays completely transparent */
128    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
129    /** Fraction of animation at which the recents thumbnail becomes completely transparent */
130    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
131
132    private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
133    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
134    private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
135    private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
136
137    private final Context mContext;
138    private final Handler mH;
139
140    private int mNextAppTransition = TRANSIT_UNSET;
141
142    private static final int NEXT_TRANSIT_TYPE_NONE = 0;
143    private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
144    private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
145    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
146    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
147    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
148    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
149    private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7;
150    private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8;
151    private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
152
153    // These are the possible states for the enter/exit activities during a thumbnail transition
154    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
155    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
156    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
157    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
158
159    private String mNextAppTransitionPackage;
160    // Used for thumbnail transitions. True if we're scaling up, false if scaling down
161    private boolean mNextAppTransitionScaleUp;
162    private IRemoteCallback mNextAppTransitionCallback;
163    private IRemoteCallback mAnimationFinishedCallback;
164    private int mNextAppTransitionEnter;
165    private int mNextAppTransitionExit;
166    private int mNextAppTransitionInPlace;
167
168    // Keyed by task id.
169    private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
170            = new SparseArray<>();
171    private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
172
173    private Rect mNextAppTransitionInsets = new Rect();
174
175    private Rect mTmpFromClipRect = new Rect();
176    private Rect mTmpToClipRect = new Rect();
177
178    private final Rect mTmpRect = new Rect();
179
180    private final static int APP_STATE_IDLE = 0;
181    private final static int APP_STATE_READY = 1;
182    private final static int APP_STATE_RUNNING = 2;
183    private final static int APP_STATE_TIMEOUT = 3;
184    private int mAppTransitionState = APP_STATE_IDLE;
185
186    private final int mConfigShortAnimTime;
187    private final Interpolator mDecelerateInterpolator;
188    private final Interpolator mThumbnailFadeInInterpolator;
189    private final Interpolator mThumbnailFadeOutInterpolator;
190    private final Interpolator mLinearOutSlowInInterpolator;
191    private final Interpolator mFastOutLinearInInterpolator;
192    private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
193
194    /** Interpolator to be used for animations that respond directly to a touch */
195    private final Interpolator mTouchResponseInterpolator =
196            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
197
198    private final int mClipRevealTranslationY;
199
200    private int mCurrentUserId = 0;
201
202    private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
203
204    AppTransition(Context context, Handler h) {
205        mContext = context;
206        mH = h;
207        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
208                com.android.internal.R.interpolator.linear_out_slow_in);
209        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
210                com.android.internal.R.interpolator.fast_out_linear_in);
211        mConfigShortAnimTime = context.getResources().getInteger(
212                com.android.internal.R.integer.config_shortAnimTime);
213        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
214                com.android.internal.R.interpolator.decelerate_cubic);
215        mThumbnailFadeInInterpolator = new Interpolator() {
216            @Override
217            public float getInterpolation(float input) {
218                // Linear response for first fraction, then complete after that.
219                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
220                    return 0f;
221                }
222                float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
223                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
224                return mFastOutLinearInInterpolator.getInterpolation(t);
225            }
226        };
227        mThumbnailFadeOutInterpolator = new Interpolator() {
228            @Override
229            public float getInterpolation(float input) {
230                // Linear response for first fraction, then complete after that.
231                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
232                    float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
233                    return mLinearOutSlowInInterpolator.getInterpolation(t);
234                }
235                return 1f;
236            }
237        };
238        mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
239                * mContext.getResources().getDisplayMetrics().density);
240    }
241
242    boolean isTransitionSet() {
243        return mNextAppTransition != TRANSIT_UNSET;
244    }
245
246    boolean isTransitionEqual(int transit) {
247        return mNextAppTransition == transit;
248    }
249
250    int getAppTransition() {
251        return mNextAppTransition;
252     }
253
254    private void setAppTransition(int transit) {
255        mNextAppTransition = transit;
256    }
257
258    boolean isReady() {
259        return mAppTransitionState == APP_STATE_READY
260                || mAppTransitionState == APP_STATE_TIMEOUT;
261    }
262
263    void setReady() {
264        mAppTransitionState = APP_STATE_READY;
265    }
266
267    boolean isRunning() {
268        return mAppTransitionState == APP_STATE_RUNNING;
269    }
270
271    void setIdle() {
272        mAppTransitionState = APP_STATE_IDLE;
273    }
274
275    boolean isTimeout() {
276        return mAppTransitionState == APP_STATE_TIMEOUT;
277    }
278
279    void setTimeout() {
280        mAppTransitionState = APP_STATE_TIMEOUT;
281    }
282
283    Bitmap getAppTransitionThumbnailHeader(int taskId) {
284        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
285        return spec != null ? spec.bitmap : null;
286    }
287
288    /** Returns whether the next thumbnail transition is aspect scaled up. */
289    boolean isNextThumbnailTransitionAspectScaled() {
290        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
291                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
292    }
293
294    /** Returns whether the next thumbnail transition is scaling up. */
295    boolean isNextThumbnailTransitionScaleUp() {
296        return mNextAppTransitionScaleUp;
297    }
298
299    private boolean prepare() {
300        if (!isRunning()) {
301            mAppTransitionState = APP_STATE_IDLE;
302            notifyAppTransitionPendingLocked();
303            return true;
304        }
305        return false;
306    }
307
308    void goodToGo(AppWindowAnimator openingAppAnimator, AppWindowAnimator closingAppAnimator) {
309        mNextAppTransition = TRANSIT_UNSET;
310        mAppTransitionState = APP_STATE_RUNNING;
311        notifyAppTransitionStartingLocked(
312                openingAppAnimator != null ? openingAppAnimator.mAppToken.token : null,
313                closingAppAnimator != null ? closingAppAnimator.mAppToken.token : null,
314                openingAppAnimator != null ? openingAppAnimator.animation : null,
315                closingAppAnimator != null ? closingAppAnimator.animation : null);
316    }
317
318    void clear() {
319        mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
320        mNextAppTransitionPackage = null;
321        mNextAppTransitionAnimationsSpecs.clear();
322    }
323
324    void freeze() {
325        setAppTransition(AppTransition.TRANSIT_UNSET);
326        clear();
327        setReady();
328        notifyAppTransitionCancelledLocked();
329    }
330
331    void registerListenerLocked(AppTransitionListener listener) {
332        mListeners.add(listener);
333    }
334
335    public void notifyAppTransitionFinishedLocked(IBinder token) {
336        for (int i = 0; i < mListeners.size(); i++) {
337            mListeners.get(i).onAppTransitionFinishedLocked(token);
338        }
339    }
340
341    private void notifyAppTransitionPendingLocked() {
342        for (int i = 0; i < mListeners.size(); i++) {
343            mListeners.get(i).onAppTransitionPendingLocked();
344        }
345    }
346
347    private void notifyAppTransitionCancelledLocked() {
348        for (int i = 0; i < mListeners.size(); i++) {
349            mListeners.get(i).onAppTransitionCancelledLocked();
350        }
351    }
352
353    private void notifyAppTransitionStartingLocked(IBinder openToken,
354            IBinder closeToken, Animation openAnimation, Animation closeAnimation) {
355        for (int i = 0; i < mListeners.size(); i++) {
356            mListeners.get(i).onAppTransitionStartingLocked(openToken, closeToken, openAnimation,
357                    closeAnimation);
358        }
359    }
360
361    private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
362        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
363                + (lp != null ? lp.packageName : null)
364                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
365        if (lp != null && lp.windowAnimations != 0) {
366            // If this is a system resource, don't try to load it from the
367            // application resources.  It is nice to avoid loading application
368            // resources if we can.
369            String packageName = lp.packageName != null ? lp.packageName : "android";
370            int resId = lp.windowAnimations;
371            if ((resId&0xFF000000) == 0x01000000) {
372                packageName = "android";
373            }
374            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
375                    + packageName);
376            return AttributeCache.instance().get(packageName, resId,
377                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
378        }
379        return null;
380    }
381
382    private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
383        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
384                + packageName + " resId=0x" + Integer.toHexString(resId));
385        if (packageName != null) {
386            if ((resId&0xFF000000) == 0x01000000) {
387                packageName = "android";
388            }
389            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
390                    + packageName);
391            return AttributeCache.instance().get(packageName, resId,
392                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
393        }
394        return null;
395    }
396
397    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
398        int anim = 0;
399        Context context = mContext;
400        if (animAttr >= 0) {
401            AttributeCache.Entry ent = getCachedAnimations(lp);
402            if (ent != null) {
403                context = ent.context;
404                anim = ent.array.getResourceId(animAttr, 0);
405            }
406        }
407        if (anim != 0) {
408            return AnimationUtils.loadAnimation(context, anim);
409        }
410        return null;
411    }
412
413    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
414        Context context = mContext;
415        if (resId >= 0) {
416            AttributeCache.Entry ent = getCachedAnimations(lp);
417            if (ent != null) {
418                context = ent.context;
419            }
420            return AnimationUtils.loadAnimation(context, resId);
421        }
422        return null;
423    }
424
425    private Animation loadAnimationRes(String packageName, int resId) {
426        int anim = 0;
427        Context context = mContext;
428        if (resId >= 0) {
429            AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
430            if (ent != null) {
431                context = ent.context;
432                anim = resId;
433            }
434        }
435        if (anim != 0) {
436            return AnimationUtils.loadAnimation(context, anim);
437        }
438        return null;
439    }
440
441    /**
442     * Compute the pivot point for an animation that is scaling from a small
443     * rect on screen to a larger rect.  The pivot point varies depending on
444     * the distance between the inner and outer edges on both sides.  This
445     * function computes the pivot point for one dimension.
446     * @param startPos  Offset from left/top edge of outer rectangle to
447     * left/top edge of inner rectangle.
448     * @param finalScale The scaling factor between the size of the outer
449     * and inner rectangles.
450     */
451    private static float computePivot(int startPos, float finalScale) {
452        final float denom = finalScale-1;
453        if (Math.abs(denom) < .0001f) {
454            return startPos;
455        }
456        return -startPos / denom;
457    }
458
459    private Animation createScaleUpAnimationLocked(int transit, boolean enter,
460            Rect containingFrame) {
461        Animation a;
462        getDefaultNextAppTransitionStartRect(mTmpRect);
463        final int appWidth = containingFrame.width();
464        final int appHeight = containingFrame.height();
465        if (enter) {
466            // Entering app zooms out from the center of the initial rect.
467            float scaleW = mTmpRect.width() / (float) appWidth;
468            float scaleH = mTmpRect.height() / (float) appHeight;
469            Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
470                    computePivot(mTmpRect.left, scaleW),
471                    computePivot(mTmpRect.right, scaleH));
472            scale.setInterpolator(mDecelerateInterpolator);
473
474            Animation alpha = new AlphaAnimation(0, 1);
475            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
476
477            AnimationSet set = new AnimationSet(false);
478            set.addAnimation(scale);
479            set.addAnimation(alpha);
480            set.setDetachWallpaper(true);
481            a = set;
482        } else  if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
483                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
484            // If we are on top of the wallpaper, we need an animation that
485            // correctly handles the wallpaper staying static behind all of
486            // the animated elements.  To do this, will just have the existing
487            // element fade out.
488            a = new AlphaAnimation(1, 0);
489            a.setDetachWallpaper(true);
490        } else {
491            // For normal animations, the exiting element just holds in place.
492            a = new AlphaAnimation(1, 1);
493        }
494
495        // Pick the desired duration.  If this is an inter-activity transition,
496        // it  is the standard duration for that.  Otherwise we use the longer
497        // task transition duration.
498        final long duration;
499        switch (transit) {
500            case TRANSIT_ACTIVITY_OPEN:
501            case TRANSIT_ACTIVITY_CLOSE:
502                duration = mConfigShortAnimTime;
503                break;
504            default:
505                duration = DEFAULT_APP_TRANSITION_DURATION;
506                break;
507        }
508        a.setDuration(duration);
509        a.setFillAfter(true);
510        a.setInterpolator(mDecelerateInterpolator);
511        a.initialize(appWidth, appHeight, appWidth, appHeight);
512        return a;
513    }
514
515    private void getDefaultNextAppTransitionStartRect(Rect rect) {
516        if (mDefaultNextAppTransitionAnimationSpec == null ||
517                mDefaultNextAppTransitionAnimationSpec.rect == null) {
518            Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
519            rect.setEmpty();
520        } else {
521            rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
522        }
523    }
524
525    void getNextAppTransitionStartRect(int taskId, Rect rect) {
526        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
527        if (spec == null || spec.rect == null) {
528            Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
529                    new Throwable());
530            rect.setEmpty();
531        } else {
532            rect.set(spec.rect);
533        }
534    }
535
536    private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height) {
537        mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
538                null /* bitmap */, new Rect(left, top, left + width, top + height));
539    }
540
541    private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
542        final Animation anim;
543        if (enter) {
544            // Reveal will expand and move faster in horizontal direction
545
546            final int appWidth = appFrame.width();
547            final int appHeight = appFrame.height();
548            // mTmpRect will contain an area around the launcher icon that was pressed. We will
549            // clip reveal from that area in the final area of the app.
550            getDefaultNextAppTransitionStartRect(mTmpRect);
551
552            float t = 0f;
553            if (appHeight > 0) {
554                t = (float) mTmpRect.left / appHeight;
555            }
556            int translationY = mClipRevealTranslationY + (int)(appHeight / 7f * t);
557
558            int centerX = mTmpRect.centerX();
559            int centerY = mTmpRect.centerY();
560            int halfWidth = mTmpRect.width() / 2;
561            int halfHeight = mTmpRect.height() / 2;
562
563            // Clip third of the from size of launch icon, expand to full width/height
564            Animation clipAnimLR = new ClipRectLRAnimation(
565                    centerX - halfWidth, centerX + halfWidth, 0, appWidth);
566            clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
567            clipAnimLR.setDuration((long) (DEFAULT_APP_TRANSITION_DURATION / 2.5f));
568
569            Animation clipAnimTB = new ClipRectTBAnimation(centerY - halfHeight - translationY,
570                    centerY + halfHeight/ 2 - translationY, 0, appHeight);
571            clipAnimTB.setInterpolator(mTouchResponseInterpolator);
572            clipAnimTB.setDuration(DEFAULT_APP_TRANSITION_DURATION);
573
574            // We might be animating entrance of a docked task, so we need the translate to account
575            // for the app frame in which the window will reside. Every other calculation here
576            // is performed as if the window started at 0,0.
577            translationY -= appFrame.top;
578            TranslateAnimation translate = new TranslateAnimation(-appFrame.left, 0, translationY,
579                    0);
580            translate.setInterpolator(mLinearOutSlowInInterpolator);
581            translate.setDuration(DEFAULT_APP_TRANSITION_DURATION);
582
583            // Quick fade-in from icon to app window
584            final int alphaDuration = DEFAULT_APP_TRANSITION_DURATION / 4;
585            AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
586            alpha.setDuration(alphaDuration);
587            alpha.setInterpolator(mLinearOutSlowInInterpolator);
588
589            AnimationSet set = new AnimationSet(false);
590            set.addAnimation(clipAnimLR);
591            set.addAnimation(clipAnimTB);
592            set.addAnimation(translate);
593            set.addAnimation(alpha);
594            set.setZAdjustment(Animation.ZORDER_TOP);
595            set.initialize(appWidth, appHeight, appWidth, appHeight);
596            anim = set;
597        } else {
598            final long duration;
599            switch (transit) {
600                case TRANSIT_ACTIVITY_OPEN:
601                case TRANSIT_ACTIVITY_CLOSE:
602                    duration = mConfigShortAnimTime;
603                    break;
604                default:
605                    duration = DEFAULT_APP_TRANSITION_DURATION;
606                    break;
607            }
608            if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
609                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
610                // If we are on top of the wallpaper, we need an animation that
611                // correctly handles the wallpaper staying static behind all of
612                // the animated elements.  To do this, will just have the existing
613                // element fade out.
614                anim = new AlphaAnimation(1, 0);
615                anim.setDetachWallpaper(true);
616            } else {
617                // For normal animations, the exiting element just holds in place.
618                anim = new AlphaAnimation(1, 1);
619            }
620            anim.setInterpolator(mDecelerateInterpolator);
621            anim.setDuration(duration);
622            anim.setFillAfter(true);
623        }
624        return anim;
625    }
626
627    /**
628     * Prepares the specified animation with a standard duration, interpolator, etc.
629     */
630    Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
631            int duration, Interpolator interpolator) {
632        if (duration > 0) {
633            a.setDuration(duration);
634        }
635        a.setFillAfter(true);
636        a.setInterpolator(interpolator);
637        a.initialize(appWidth, appHeight, appWidth, appHeight);
638        return a;
639    }
640
641    /**
642     * Prepares the specified animation with a standard duration, interpolator, etc.
643     */
644    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
645        // Pick the desired duration.  If this is an inter-activity transition,
646        // it  is the standard duration for that.  Otherwise we use the longer
647        // task transition duration.
648        final int duration;
649        switch (transit) {
650            case TRANSIT_ACTIVITY_OPEN:
651            case TRANSIT_ACTIVITY_CLOSE:
652                duration = mConfigShortAnimTime;
653                break;
654            default:
655                duration = DEFAULT_APP_TRANSITION_DURATION;
656                break;
657        }
658        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
659                mDecelerateInterpolator);
660    }
661
662    /**
663     * Return the current thumbnail transition state.
664     */
665    int getThumbnailTransitionState(boolean enter) {
666        if (enter) {
667            if (mNextAppTransitionScaleUp) {
668                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
669            } else {
670                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
671            }
672        } else {
673            if (mNextAppTransitionScaleUp) {
674                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
675            } else {
676                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
677            }
678        }
679    }
680
681    /**
682     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
683     * when a thumbnail is specified with the pending animation override.
684     */
685    Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, Bitmap thumbnailHeader,
686            final int taskId) {
687        Animation a;
688        final int thumbWidthI = thumbnailHeader.getWidth();
689        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
690        final int thumbHeightI = thumbnailHeader.getHeight();
691        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
692        final int appWidth = appRect.width();
693
694        float scaleW = appWidth / thumbWidth;
695        float unscaledHeight = thumbHeight * scaleW;
696        getNextAppTransitionStartRect(taskId, mTmpRect);
697        float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f;
698        if (mNextAppTransitionScaleUp) {
699            // Animation up from the thumbnail to the full screen
700            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
701                    mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
702            scale.setInterpolator(mTouchResponseInterpolator);
703            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
704            Animation alpha = new AlphaAnimation(1, 0);
705            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
706            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
707            final float toX = appRect.left + appRect.width() / 2 -
708                    (mTmpRect.left + thumbWidth / 2);
709            final float toY = appRect.top + mNextAppTransitionInsets.top + -unscaledStartY;
710            Animation translate = new TranslateAnimation(0, toX, 0, toY);
711            translate.setInterpolator(mTouchResponseInterpolator);
712            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
713
714            // This AnimationSet uses the Interpolators assigned above.
715            AnimationSet set = new AnimationSet(false);
716            set.addAnimation(scale);
717            set.addAnimation(alpha);
718            set.addAnimation(translate);
719            a = set;
720        } else {
721            // Animation down from the full screen to the thumbnail
722            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
723                    mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
724            scale.setInterpolator(mTouchResponseInterpolator);
725            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
726            Animation alpha = new AlphaAnimation(0f, 1f);
727            alpha.setInterpolator(mThumbnailFadeInInterpolator);
728            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
729            Animation translate = new TranslateAnimation(0, 0, -unscaledStartY +
730                    mNextAppTransitionInsets.top, 0);
731            translate.setInterpolator(mTouchResponseInterpolator);
732            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
733
734            // This AnimationSet uses the Interpolators assigned above.
735            AnimationSet set = new AnimationSet(false);
736            set.addAnimation(scale);
737            set.addAnimation(alpha);
738            set.addAnimation(translate);
739            a = set;
740
741        }
742        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
743                mTouchResponseInterpolator);
744    }
745
746    /**
747     * This alternate animation is created when we are doing a thumbnail transition, for the
748     * activity that is leaving, and the activity that is entering.
749     */
750    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
751            int orientation, int transit, Rect containingFrame, Rect contentInsets,
752            @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
753        Animation a;
754        final int appWidth = containingFrame.width();
755        final int appHeight = containingFrame.height();
756        getDefaultNextAppTransitionStartRect(mTmpRect);
757        final int thumbWidthI = mTmpRect.width();
758        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
759        final int thumbHeightI = mTmpRect.height();
760        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
761
762        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
763        float scale = 1f;
764        int scaledTopDecor = 0;
765
766        switch (thumbTransitState) {
767            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
768                if (freeform) {
769                    a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
770                            containingFrame, surfaceInsets, taskId);
771                } else {
772                    mTmpFromClipRect.set(containingFrame);
773                    // exclude top screen decor (status bar) region from the source clip.
774                    mTmpFromClipRect.top = contentInsets.top;
775                    // App window scaling up to become full screen
776                    mTmpToClipRect.set(containingFrame);
777                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
778                        // In portrait, we scale the width and clip to the top/left square
779                        scale = thumbWidth / appWidth;
780                        scaledTopDecor = (int) (scale * contentInsets.top);
781                        int unscaledThumbHeight = (int) (thumbHeight / scale);
782                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
783                    } else {
784                        // In landscape, we scale the height and clip to the top/left square. We
785                        // only scale the part that is not covered by status bar and the nav bar.
786                        scale = thumbHeight / (appHeight - contentInsets.top
787                                - contentInsets.bottom);
788                        scaledTopDecor = (int) (scale * contentInsets.top);
789                        int unscaledThumbWidth = (int) (thumbWidth / scale);
790                        mTmpFromClipRect.right = mTmpFromClipRect.left + unscaledThumbWidth;
791                        // This removes the navigation bar from the first frame, so it better
792                        // matches the thumbnail. We need to do this explicitly in landscape,
793                        // because in portrait we already crop vertically.
794                        mTmpFromClipRect.bottom = mTmpFromClipRect.bottom - contentInsets.bottom;
795                    }
796
797                    mNextAppTransitionInsets.set(contentInsets);
798
799                    Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
800                            computePivot(mTmpRect.left, scale),
801                            computePivot(mTmpRect.top, scale));
802                    Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
803                    Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
804
805                    AnimationSet set = new AnimationSet(true);
806                    set.addAnimation(clipAnim);
807                    set.addAnimation(scaleAnim);
808                    set.addAnimation(translateAnim);
809                    a = set;
810                }
811                break;
812            }
813            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
814                // Previous app window during the scale up
815                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
816                    // Fade out the source activity if we are animating to a wallpaper
817                    // activity.
818                    a = new AlphaAnimation(1, 0);
819                } else {
820                    a = new AlphaAnimation(1, 1);
821                }
822                break;
823            }
824            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
825                // Target app window during the scale down
826                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
827                    // Fade in the destination activity if we are animating from a wallpaper
828                    // activity.
829                    a = new AlphaAnimation(0, 1);
830                } else {
831                    a = new AlphaAnimation(1, 1);
832                }
833                break;
834            }
835            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
836                // App window scaling down from full screen
837                if (freeform) {
838                    a = createAspectScaledThumbnailExitFreeformAnimationLocked(
839                            containingFrame, surfaceInsets, taskId);
840                } else {
841                    mTmpFromClipRect.set(containingFrame);
842                    mTmpToClipRect.set(containingFrame);
843                    // exclude top screen decor (status bar) region from the destination clip.
844                    mTmpToClipRect.top = contentInsets.top;
845                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
846                        // In portrait, we scale the width and clip to the top/left square
847                        scale = thumbWidth / appWidth;
848                        scaledTopDecor = (int) (scale * contentInsets.top);
849                        int unscaledThumbHeight = (int) (thumbHeight / scale);
850                        mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
851                    } else {
852                        // In landscape, we scale the height and clip to the top/left square. We only
853                        // scale the part that is not covered by status bar and the nav bar.
854                        scale = thumbHeight / (appHeight - contentInsets.top - contentInsets.bottom);
855                        scaledTopDecor = (int) (scale * contentInsets.top);
856                        int unscaledThumbWidth = (int) (thumbWidth / scale);
857                        mTmpToClipRect.right = mTmpToClipRect.left + unscaledThumbWidth;
858                        // This removes the navigation bar from the last frame, so it better matches the
859                        // thumbnail. We need to do this explicitly in landscape, because in portrait we
860                        // already crop vertically.
861                        mTmpToClipRect.bottom = mTmpToClipRect.bottom - contentInsets.bottom;
862                    }
863
864                    mNextAppTransitionInsets.set(contentInsets);
865
866                    Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
867                            computePivot(mTmpRect.left, scale),
868                            computePivot(mTmpRect.top, scale));
869                    Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
870                    Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
871
872                    AnimationSet set = new AnimationSet(true);
873                    set.addAnimation(clipAnim);
874                    set.addAnimation(scaleAnim);
875                    set.addAnimation(translateAnim);
876
877                    a = set;
878                    a.setZAdjustment(Animation.ZORDER_TOP);
879                }
880                break;
881            }
882            default:
883                throw new RuntimeException("Invalid thumbnail transition state");
884        }
885
886        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
887                THUMBNAIL_APP_TRANSITION_DURATION);
888        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
889                mTouchResponseInterpolator);
890    }
891
892    private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
893            @Nullable Rect surfaceInsets, int taskId) {
894        getNextAppTransitionStartRect(taskId, mTmpRect);
895        return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
896                true);
897    }
898
899    private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
900            @Nullable Rect surfaceInsets, int taskId) {
901        getNextAppTransitionStartRect(taskId, mTmpRect);
902        return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
903                false);
904    }
905
906    private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
907            Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
908        final float sourceWidth = sourceFrame.width();
909        final float sourceHeight = sourceFrame.height();
910        final float destWidth = destFrame.width();
911        final float destHeight = destFrame.height();
912        final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
913        final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
914        AnimationSet set = new AnimationSet(true);
915        final int surfaceInsetsH = surfaceInsets == null
916                ? 0 : surfaceInsets.left + surfaceInsets.right;
917        final int surfaceInsetsV = surfaceInsets == null
918                ? 0 : surfaceInsets.top + surfaceInsets.bottom;
919        // We want the scaling to happen from the center of the surface. In order to achieve that,
920        // we need to account for surface insets that will be used to enlarge the surface.
921        final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
922        final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
923        final ScaleAnimation scale = enter ?
924                new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
925                : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
926        final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
927        final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
928        final int destHCenter = destFrame.left + destFrame.width() / 2;
929        final int destVCenter = destFrame.top + destFrame.height() / 2;
930        final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
931        final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
932        final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
933                : new TranslateAnimation(0, fromX, 0, fromY);
934        set.addAnimation(scale);
935        set.addAnimation(translation);
936
937        final IRemoteCallback callback = mAnimationFinishedCallback;
938        if (callback != null) {
939            set.setAnimationListener(new Animation.AnimationListener() {
940                @Override
941                public void onAnimationStart(Animation animation) { }
942
943                @Override
944                public void onAnimationEnd(Animation animation) {
945                    mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
946                }
947
948                @Override
949                public void onAnimationRepeat(Animation animation) { }
950            });
951        }
952        return set;
953    }
954
955    /**
956     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
957     * when a thumbnail is specified with the pending animation override.
958     */
959    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
960            Bitmap thumbnailHeader) {
961        Animation a;
962        getDefaultNextAppTransitionStartRect(mTmpRect);
963        final int thumbWidthI = thumbnailHeader.getWidth();
964        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
965        final int thumbHeightI = thumbnailHeader.getHeight();
966        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
967
968        if (mNextAppTransitionScaleUp) {
969            // Animation for the thumbnail zooming from its initial size to the full screen
970            float scaleW = appWidth / thumbWidth;
971            float scaleH = appHeight / thumbHeight;
972            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
973                    computePivot(mTmpRect.left, 1 / scaleW),
974                    computePivot(mTmpRect.top, 1 / scaleH));
975            scale.setInterpolator(mDecelerateInterpolator);
976
977            Animation alpha = new AlphaAnimation(1, 0);
978            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
979
980            // This AnimationSet uses the Interpolators assigned above.
981            AnimationSet set = new AnimationSet(false);
982            set.addAnimation(scale);
983            set.addAnimation(alpha);
984            a = set;
985        } else {
986            // Animation for the thumbnail zooming down from the full screen to its final size
987            float scaleW = appWidth / thumbWidth;
988            float scaleH = appHeight / thumbHeight;
989            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
990                    computePivot(mTmpRect.left, 1 / scaleW),
991                    computePivot(mTmpRect.top, 1 / scaleH));
992        }
993
994        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
995    }
996
997    /**
998     * This animation is created when we are doing a thumbnail transition, for the activity that is
999     * leaving, and the activity that is entering.
1000     */
1001    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
1002            int transit, int taskId) {
1003        final int appWidth = containingFrame.width();
1004        final int appHeight = containingFrame.height();
1005        Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
1006        Animation a;
1007        getDefaultNextAppTransitionStartRect(mTmpRect);
1008        final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
1009        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1010        final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
1011        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1012
1013        switch (thumbTransitState) {
1014            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
1015                // Entering app scales up with the thumbnail
1016                float scaleW = thumbWidth / appWidth;
1017                float scaleH = thumbHeight / appHeight;
1018                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1019                        computePivot(mTmpRect.left, scaleW),
1020                        computePivot(mTmpRect.top, scaleH));
1021                break;
1022            }
1023            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1024                // Exiting app while the thumbnail is scaling up should fade or stay in place
1025                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1026                    // Fade out while bringing up selected activity. This keeps the
1027                    // current activity from showing through a launching wallpaper
1028                    // activity.
1029                    a = new AlphaAnimation(1, 0);
1030                } else {
1031                    // noop animation
1032                    a = new AlphaAnimation(1, 1);
1033                }
1034                break;
1035            }
1036            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1037                // Entering the other app, it should just be visible while we scale the thumbnail
1038                // down above it
1039                a = new AlphaAnimation(1, 1);
1040                break;
1041            }
1042            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1043                // Exiting the current app, the app should scale down with the thumbnail
1044                float scaleW = thumbWidth / appWidth;
1045                float scaleH = thumbHeight / appHeight;
1046                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1047                        computePivot(mTmpRect.left, scaleW),
1048                        computePivot(mTmpRect.top, scaleH));
1049
1050                Animation alpha = new AlphaAnimation(1, 0);
1051
1052                AnimationSet set = new AnimationSet(true);
1053                set.addAnimation(scale);
1054                set.addAnimation(alpha);
1055                set.setZAdjustment(Animation.ZORDER_TOP);
1056                a = set;
1057                break;
1058            }
1059            default:
1060                throw new RuntimeException("Invalid thumbnail transition state");
1061        }
1062
1063        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1064    }
1065
1066    private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
1067        getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
1068        final int left = mTmpFromClipRect.left;
1069        final int top = mTmpFromClipRect.top;
1070        mTmpFromClipRect.offset(-left, -top);
1071        // TODO: Isn't that strange that we ignore exact position of the containingFrame?
1072        mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
1073        AnimationSet set = new AnimationSet(true);
1074        float fromWidth = mTmpFromClipRect.width();
1075        float toWidth = mTmpToClipRect.width();
1076        float fromHeight = mTmpFromClipRect.height();
1077        // While the window might span the whole display, the actual content will be cropped to the
1078        // system decoration frame, for example when the window is docked. We need to take into
1079        // account the visible height when constructing the animation.
1080        float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
1081        int translateAdjustment = 0;
1082        if (fromWidth <= toWidth && fromHeight <= toHeight) {
1083            // The final window is larger in both dimensions than current window (e.g. we are
1084            // maximizing), so we can simply unclip the new window and there will be no disappearing
1085            // frame.
1086            set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
1087        } else {
1088            // The disappearing window has one larger dimension. We need to apply scaling, so the
1089            // first frame of the entry animation matches the old window.
1090            set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
1091            // We might not be going exactly full screen, but instead be aligned under the status
1092            // bar using cropping. We still need to account for the cropped part, which will also
1093            // be scaled.
1094            translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
1095        }
1096
1097        // We animate the translation from the old position of the removed window, to the new
1098        // position of the added window. The latter might not be full screen, for example docked for
1099        // docked windows.
1100        TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
1101                0, top - containingFrame.top - translateAdjustment, 0);
1102        set.addAnimation(translate);
1103        set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
1104        set.setZAdjustment(Animation.ZORDER_TOP);
1105        return set;
1106    }
1107
1108    /**
1109     * @return true if and only if the first frame of the transition can be skipped, i.e. the first
1110     *         frame of the transition doesn't change the visuals on screen, so we can start
1111     *         directly with the second one
1112     */
1113    boolean canSkipFirstFrame() {
1114        return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
1115                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
1116                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1117    }
1118
1119    /**
1120     *
1121     * @param frame These are the bounds of the window when it finishes the animation. This is where
1122     *              the animation must usually finish in entrance animation, as the next frame will
1123     *              display the window at these coordinates. In case of exit animation, this is
1124     *              where the animation must start, as the frame before the animation is displaying
1125     *              the window at these bounds.
1126     * @param insets Knowing where the window will be positioned is not enough. Some parts of the
1127     *               window might be obscured, usually by the system windows (status bar and
1128     *               navigation bar) and we use content insets to convey that information. This
1129     *               usually affects the animation aspects vertically, as the system decoration is
1130     *               at the top and the bottom. For example when we animate from full screen to
1131     *               recents, we want to exclude the covered parts, because they won't match the
1132     *               thumbnail after the last frame is executed.
1133     * @param surfaceInsets In rare situation the surface is larger than the content and we need to
1134     *                      know about this to make the animation frames match. We currently use
1135     *                      this for freeform windows, which have larger surfaces to display
1136     *                      shadows. When we animate them from recents, we want to match the content
1137     *                      to the recents thumbnail and hence need to account for the surface being
1138     *                      bigger.
1139     */
1140    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
1141            int orientation, Rect frame, Rect insets, @Nullable Rect surfaceInsets,
1142            boolean isVoiceInteraction, boolean freeform, int taskId) {
1143        Animation a;
1144        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
1145                || transit == TRANSIT_TASK_OPEN
1146                || transit == TRANSIT_TASK_TO_FRONT)) {
1147            a = loadAnimationRes(lp, enter
1148                    ? com.android.internal.R.anim.voice_activity_open_enter
1149                    : com.android.internal.R.anim.voice_activity_open_exit);
1150            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1151                    "applyAnimation voice:"
1152                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1153                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1154        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
1155                || transit == TRANSIT_TASK_CLOSE
1156                || transit == TRANSIT_TASK_TO_BACK)) {
1157            a = loadAnimationRes(lp, enter
1158                    ? com.android.internal.R.anim.voice_activity_close_enter
1159                    : com.android.internal.R.anim.voice_activity_close_exit);
1160            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1161                    "applyAnimation voice:"
1162                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1163                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1164        } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
1165            a = createRelaunchAnimation(frame, insets);
1166            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1167                    "applyAnimation:"
1168                    + " anim=" + a + " nextAppTransition=" + mNextAppTransition
1169                    + " transit=" + appTransitionToString(transit)
1170                    + " Callers=" + Debug.getCallers(3));
1171        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
1172            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
1173                    mNextAppTransitionEnter : mNextAppTransitionExit);
1174            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1175                    "applyAnimation:"
1176                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
1177                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1178                    + " Callers=" + Debug.getCallers(3));
1179        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
1180            a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
1181            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1182                    "applyAnimation:"
1183                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
1184                    + " transit=" + appTransitionToString(transit)
1185                    + " Callers=" + Debug.getCallers(3));
1186        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
1187            a = createClipRevealAnimationLocked(transit, enter, frame);
1188            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1189                    "applyAnimation:"
1190                            + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
1191                            + " transit=" + appTransitionToString(transit)
1192                            + " Callers=" + Debug.getCallers(3));
1193        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
1194            a = createScaleUpAnimationLocked(transit, enter, frame);
1195            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1196                    "applyAnimation:"
1197                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
1198                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1199                    + " Callers=" + Debug.getCallers(3));
1200        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
1201                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
1202            mNextAppTransitionScaleUp =
1203                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
1204            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
1205                    frame, transit, taskId);
1206            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1207                String animName = mNextAppTransitionScaleUp ?
1208                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
1209                Slog.v(TAG, "applyAnimation:"
1210                        + " anim=" + a + " nextAppTransition=" + animName
1211                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1212                        + " Callers=" + Debug.getCallers(3));
1213            }
1214        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
1215                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
1216            mNextAppTransitionScaleUp =
1217                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
1218            a = createAspectScaledThumbnailEnterExitAnimationLocked(
1219                    getThumbnailTransitionState(enter), orientation, transit, frame,
1220                    insets, surfaceInsets, freeform, taskId);
1221            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1222                String animName = mNextAppTransitionScaleUp ?
1223                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
1224                Slog.v(TAG, "applyAnimation:"
1225                        + " anim=" + a + " nextAppTransition=" + animName
1226                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1227                        + " Callers=" + Debug.getCallers(3));
1228            }
1229        } else {
1230            int animAttr = 0;
1231            switch (transit) {
1232                case TRANSIT_ACTIVITY_OPEN:
1233                    animAttr = enter
1234                            ? WindowAnimation_activityOpenEnterAnimation
1235                            : WindowAnimation_activityOpenExitAnimation;
1236                    break;
1237                case TRANSIT_ACTIVITY_CLOSE:
1238                    animAttr = enter
1239                            ? WindowAnimation_activityCloseEnterAnimation
1240                            : WindowAnimation_activityCloseExitAnimation;
1241                    break;
1242                case TRANSIT_TASK_OPEN:
1243                    animAttr = enter
1244                            ? WindowAnimation_taskOpenEnterAnimation
1245                            : WindowAnimation_taskOpenExitAnimation;
1246                    break;
1247                case TRANSIT_TASK_CLOSE:
1248                    animAttr = enter
1249                            ? WindowAnimation_taskCloseEnterAnimation
1250                            : WindowAnimation_taskCloseExitAnimation;
1251                    break;
1252                case TRANSIT_TASK_TO_FRONT:
1253                    animAttr = enter
1254                            ? WindowAnimation_taskToFrontEnterAnimation
1255                            : WindowAnimation_taskToFrontExitAnimation;
1256                    break;
1257                case TRANSIT_TASK_TO_BACK:
1258                    animAttr = enter
1259                            ? WindowAnimation_taskToBackEnterAnimation
1260                            : WindowAnimation_taskToBackExitAnimation;
1261                    break;
1262                case TRANSIT_WALLPAPER_OPEN:
1263                    animAttr = enter
1264                            ? WindowAnimation_wallpaperOpenEnterAnimation
1265                            : WindowAnimation_wallpaperOpenExitAnimation;
1266                    break;
1267                case TRANSIT_WALLPAPER_CLOSE:
1268                    animAttr = enter
1269                            ? WindowAnimation_wallpaperCloseEnterAnimation
1270                            : WindowAnimation_wallpaperCloseExitAnimation;
1271                    break;
1272                case TRANSIT_WALLPAPER_INTRA_OPEN:
1273                    animAttr = enter
1274                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
1275                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
1276                    break;
1277                case TRANSIT_WALLPAPER_INTRA_CLOSE:
1278                    animAttr = enter
1279                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
1280                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
1281                    break;
1282                case TRANSIT_TASK_OPEN_BEHIND:
1283                    animAttr = enter
1284                            ? WindowAnimation_launchTaskBehindSourceAnimation
1285                            : WindowAnimation_launchTaskBehindTargetAnimation;
1286            }
1287            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
1288            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1289                    "applyAnimation:"
1290                    + " anim=" + a
1291                    + " animAttr=0x" + Integer.toHexString(animAttr)
1292                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1293                    + " Callers=" + Debug.getCallers(3));
1294        }
1295        return a;
1296    }
1297
1298    void postAnimationCallback() {
1299        if (mNextAppTransitionCallback != null) {
1300            mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
1301            mNextAppTransitionCallback = null;
1302        }
1303    }
1304
1305    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
1306            IRemoteCallback startedCallback) {
1307        if (isTransitionSet()) {
1308            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
1309            mNextAppTransitionPackage = packageName;
1310            mNextAppTransitionAnimationsSpecs.clear();
1311            mNextAppTransitionEnter = enterAnim;
1312            mNextAppTransitionExit = exitAnim;
1313            postAnimationCallback();
1314            mNextAppTransitionCallback = startedCallback;
1315        } else {
1316            postAnimationCallback();
1317        }
1318    }
1319
1320    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
1321            int startHeight) {
1322        if (isTransitionSet()) {
1323            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
1324            mNextAppTransitionPackage = null;
1325            mNextAppTransitionAnimationsSpecs.clear();
1326            putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
1327                    startY + startHeight);
1328            postAnimationCallback();
1329            mNextAppTransitionCallback = null;
1330        }
1331    }
1332
1333    void overridePendingAppTransitionClipReveal(int startX, int startY,
1334                                                int startWidth, int startHeight) {
1335        if (isTransitionSet()) {
1336            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1337            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight);
1338            postAnimationCallback();
1339            mNextAppTransitionCallback = null;
1340        }
1341    }
1342
1343    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
1344                                           IRemoteCallback startedCallback, boolean scaleUp) {
1345        if (isTransitionSet()) {
1346            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
1347                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
1348            mNextAppTransitionPackage = null;
1349            mNextAppTransitionAnimationsSpecs.clear();
1350            mNextAppTransitionScaleUp = scaleUp;
1351            putDefaultNextAppTransitionCoordinates(startX, startY, 0 ,0);
1352            postAnimationCallback();
1353            mNextAppTransitionCallback = startedCallback;
1354        } else {
1355            postAnimationCallback();
1356        }
1357    }
1358
1359    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
1360            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
1361        if (isTransitionSet()) {
1362            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1363                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1364            mNextAppTransitionPackage = null;
1365            mNextAppTransitionAnimationsSpecs.clear();
1366            mNextAppTransitionScaleUp = scaleUp;
1367            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight);
1368            postAnimationCallback();
1369            mNextAppTransitionCallback = startedCallback;
1370        } else {
1371            postAnimationCallback();
1372        }
1373    }
1374
1375    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
1376            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
1377            boolean scaleUp) {
1378        if (isTransitionSet()) {
1379            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1380                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1381            mNextAppTransitionPackage = null;
1382            mDefaultNextAppTransitionAnimationSpec = null;
1383            mNextAppTransitionAnimationsSpecs.clear();
1384            mNextAppTransitionScaleUp = scaleUp;
1385            for (int i = 0; i < specs.length; i++) {
1386                AppTransitionAnimationSpec spec = specs[i];
1387                if (spec != null) {
1388                    mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
1389                    if (i == 0) {
1390                        // In full screen mode, the transition code depends on the default spec to
1391                        // be set.
1392                        Rect rect = spec.rect;
1393                        putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
1394                                rect.height());
1395                    }
1396                }
1397            }
1398            postAnimationCallback();
1399            mNextAppTransitionCallback = onAnimationStartedCallback;
1400            mAnimationFinishedCallback = onAnimationFinishedCallback;
1401        } else {
1402            postAnimationCallback();
1403        }
1404    }
1405
1406    void overrideInPlaceAppTransition(String packageName, int anim) {
1407        if (isTransitionSet()) {
1408            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
1409            mNextAppTransitionPackage = packageName;
1410            mNextAppTransitionInPlace = anim;
1411        } else {
1412            postAnimationCallback();
1413        }
1414    }
1415
1416    @Override
1417    public String toString() {
1418        return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
1419    }
1420
1421    /**
1422     * Returns the human readable name of a window transition.
1423     *
1424     * @param transition The window transition.
1425     * @return The transition symbolic name.
1426     */
1427    public static String appTransitionToString(int transition) {
1428        switch (transition) {
1429            case TRANSIT_UNSET: {
1430                return "TRANSIT_UNSET";
1431            }
1432            case TRANSIT_NONE: {
1433                return "TRANSIT_NONE";
1434            }
1435            case TRANSIT_ACTIVITY_OPEN: {
1436                return "TRANSIT_ACTIVITY_OPEN";
1437            }
1438            case TRANSIT_ACTIVITY_CLOSE: {
1439                return "TRANSIT_ACTIVITY_CLOSE";
1440            }
1441            case TRANSIT_TASK_OPEN: {
1442                return "TRANSIT_TASK_OPEN";
1443            }
1444            case TRANSIT_TASK_CLOSE: {
1445                return "TRANSIT_TASK_CLOSE";
1446            }
1447            case TRANSIT_TASK_TO_FRONT: {
1448                return "TRANSIT_TASK_TO_FRONT";
1449            }
1450            case TRANSIT_TASK_TO_BACK: {
1451                return "TRANSIT_TASK_TO_BACK";
1452            }
1453            case TRANSIT_WALLPAPER_CLOSE: {
1454                return "TRANSIT_WALLPAPER_CLOSE";
1455            }
1456            case TRANSIT_WALLPAPER_OPEN: {
1457                return "TRANSIT_WALLPAPER_OPEN";
1458            }
1459            case TRANSIT_WALLPAPER_INTRA_OPEN: {
1460                return "TRANSIT_WALLPAPER_INTRA_OPEN";
1461            }
1462            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1463                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1464            }
1465            case TRANSIT_TASK_OPEN_BEHIND: {
1466                return "TRANSIT_TASK_OPEN_BEHIND";
1467            }
1468            case TRANSIT_ACTIVITY_RELAUNCH: {
1469                return "TRANSIT_ACTIVITY_RELAUNCH";
1470            }
1471            default: {
1472                return "<UNKNOWN>";
1473            }
1474        }
1475    }
1476
1477    private String appStateToString() {
1478        switch (mAppTransitionState) {
1479            case APP_STATE_IDLE:
1480                return "APP_STATE_IDLE";
1481            case APP_STATE_READY:
1482                return "APP_STATE_READY";
1483            case APP_STATE_RUNNING:
1484                return "APP_STATE_RUNNING";
1485            case APP_STATE_TIMEOUT:
1486                return "APP_STATE_TIMEOUT";
1487            default:
1488                return "unknown state=" + mAppTransitionState;
1489        }
1490    }
1491
1492    private String transitTypeToString() {
1493        switch (mNextAppTransitionType) {
1494            case NEXT_TRANSIT_TYPE_NONE:
1495                return "NEXT_TRANSIT_TYPE_NONE";
1496            case NEXT_TRANSIT_TYPE_CUSTOM:
1497                return "NEXT_TRANSIT_TYPE_CUSTOM";
1498            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1499                return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE";
1500            case NEXT_TRANSIT_TYPE_SCALE_UP:
1501                return "NEXT_TRANSIT_TYPE_SCALE_UP";
1502            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1503                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1504            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1505                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1506            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1507                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1508            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1509                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1510            default:
1511                return "unknown type=" + mNextAppTransitionType;
1512        }
1513    }
1514
1515    @Override
1516    public void dump(PrintWriter pw, String prefix) {
1517        pw.print(prefix); pw.println(this);
1518        pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString());
1519        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1520            pw.print(prefix); pw.print("mNextAppTransitionType=");
1521                    pw.println(transitTypeToString());
1522        }
1523        switch (mNextAppTransitionType) {
1524            case NEXT_TRANSIT_TYPE_CUSTOM:
1525                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1526                        pw.println(mNextAppTransitionPackage);
1527                pw.print(prefix); pw.print("mNextAppTransitionEnter=0x");
1528                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
1529                        pw.print(" mNextAppTransitionExit=0x");
1530                        pw.println(Integer.toHexString(mNextAppTransitionExit));
1531                break;
1532            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1533                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1534                        pw.println(mNextAppTransitionPackage);
1535                pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
1536                        pw.print(Integer.toHexString(mNextAppTransitionInPlace));
1537                break;
1538            case NEXT_TRANSIT_TYPE_SCALE_UP: {
1539                getDefaultNextAppTransitionStartRect(mTmpRect);
1540                pw.print(prefix); pw.print("mNextAppTransitionStartX=");
1541                        pw.print(mTmpRect.left);
1542                        pw.print(" mNextAppTransitionStartY=");
1543                        pw.println(mTmpRect.top);
1544                pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
1545                        pw.print(mTmpRect.width());
1546                        pw.print(" mNextAppTransitionStartHeight=");
1547                        pw.println(mTmpRect.height());
1548                break;
1549            }
1550            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1551            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1552            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1553            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
1554                pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
1555                        pw.println(mDefaultNextAppTransitionAnimationSpec);
1556                pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
1557                        pw.println(mNextAppTransitionAnimationsSpecs);
1558                pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
1559                        pw.println(mNextAppTransitionScaleUp);
1560                break;
1561            }
1562        }
1563        if (mNextAppTransitionCallback != null) {
1564            pw.print(prefix); pw.print("mNextAppTransitionCallback=");
1565                    pw.println(mNextAppTransitionCallback);
1566        }
1567    }
1568
1569    public void setCurrentUser(int newUserId) {
1570        mCurrentUserId = newUserId;
1571    }
1572
1573    /**
1574     * @return true if transition is not running and should not be skipped, false if transition is
1575     *         already running
1576     */
1577    boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
1578        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
1579                + " transit=" + appTransitionToString(transit)
1580                + " " + this
1581                + " alwaysKeepCurrent=" + alwaysKeepCurrent
1582                + " Callers=" + Debug.getCallers(3));
1583        if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
1584            setAppTransition(transit);
1585        } else if (!alwaysKeepCurrent) {
1586            if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
1587                // Opening a new task always supersedes a close for the anim.
1588                setAppTransition(transit);
1589            } else if (transit == TRANSIT_ACTIVITY_OPEN
1590                    && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
1591                // Opening a new activity always supersedes a close for the anim.
1592                setAppTransition(transit);
1593            }
1594        }
1595        boolean prepared = prepare();
1596        if (isTransitionSet()) {
1597            mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
1598            mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
1599        }
1600        return prepared;
1601    }
1602}
1603