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