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