AppTransition.java revision ab79fce2e71b6816b2b88b826ca723b3591f1e26
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 android.content.Context;
20import android.content.res.Configuration;
21import android.graphics.Bitmap;
22import android.graphics.Rect;
23import android.os.Debug;
24import android.os.Handler;
25import android.os.IRemoteCallback;
26import android.util.Slog;
27import android.view.WindowManager;
28import android.view.animation.AlphaAnimation;
29import android.view.animation.Animation;
30import android.view.animation.AnimationSet;
31import android.view.animation.AnimationUtils;
32import android.view.animation.ClipRectAnimation;
33import android.view.animation.Interpolator;
34import android.view.animation.ScaleAnimation;
35import android.view.animation.TranslateAnimation;
36import com.android.internal.util.DumpUtils.Dump;
37import com.android.server.AttributeCache;
38import com.android.server.wm.WindowManagerService.H;
39
40import java.io.PrintWriter;
41
42import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
43import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
44import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
45import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
46import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
47import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
48import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
49import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
50import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
51import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
52import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
53import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
54import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation;
55import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
56import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
57import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
58import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation;
59import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
60import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation;
61import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
62import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation;
63import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
64
65// State management of app transitions.  When we are preparing for a
66// transition, mNextAppTransition will be the kind of transition to
67// perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
68// mOpeningApps and mClosingApps are the lists of tokens that will be
69// made visible or hidden at the next transition.
70public class AppTransition implements Dump {
71    private static final String TAG = "AppTransition";
72    private static final boolean DEBUG_APP_TRANSITIONS =
73            WindowManagerService.DEBUG_APP_TRANSITIONS;
74    private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
75
76
77    /** Not set up for a transition. */
78    public static final int TRANSIT_UNSET = -1;
79    /** No animation for transition. */
80    public static final int TRANSIT_NONE = 0;
81    /** A window in a new activity is being opened on top of an existing one in the same task. */
82    public static final int TRANSIT_ACTIVITY_OPEN = 6;
83    /** The window in the top-most activity is being closed to reveal the
84     * previous activity in the same task. */
85    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
86    /** A window in a new task is being opened on top of an existing one
87     * in another activity's task. */
88    public static final int TRANSIT_TASK_OPEN = 8;
89    /** A window in the top-most activity is being closed to reveal the
90     * previous activity in a different task. */
91    public static final int TRANSIT_TASK_CLOSE = 9;
92    /** A window in an existing task is being displayed on top of an existing one
93     * in another activity's task. */
94    public static final int TRANSIT_TASK_TO_FRONT = 10;
95    /** A window in an existing task is being put below all other tasks. */
96    public static final int TRANSIT_TASK_TO_BACK = 11;
97    /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
98     * does, effectively closing the wallpaper. */
99    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
100    /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
101     * effectively opening the wallpaper. */
102    public static final int TRANSIT_WALLPAPER_OPEN = 13;
103    /** A window in a new activity is being opened on top of an existing one, and both are on top
104     * of the wallpaper. */
105    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
106    /** The window in the top-most activity is being closed to reveal the previous activity, and
107     * both are on top of the wallpaper. */
108    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
109    /** A window in a new task is being opened behind an existing one in another activity's task.
110     * The new window will show briefly and then be gone. */
111    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
112
113    /** Fraction of animation at which the recents thumbnail stays completely transparent */
114    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.7f;
115    /** Fraction of animation at which the recents thumbnail becomes completely transparent */
116    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.3f;
117
118    private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
119    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 300;
120    private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 325;
121
122    private final Context mContext;
123    private final Handler mH;
124
125    private int mNextAppTransition = TRANSIT_UNSET;
126
127    private static final int NEXT_TRANSIT_TYPE_NONE = 0;
128    private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
129    private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
130    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
131    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
132    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
133    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
134    private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
135
136    // These are the possible states for the enter/exit activities during a thumbnail transition
137    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
138    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
139    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
140    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
141
142    private String mNextAppTransitionPackage;
143    private Bitmap mNextAppTransitionThumbnail;
144    // Used for thumbnail transitions. True if we're scaling up, false if scaling down
145    private boolean mNextAppTransitionScaleUp;
146    private IRemoteCallback mNextAppTransitionCallback;
147    private int mNextAppTransitionEnter;
148    private int mNextAppTransitionExit;
149    private int mNextAppTransitionStartX;
150    private int mNextAppTransitionStartY;
151    private int mNextAppTransitionStartWidth;
152    private int mNextAppTransitionStartHeight;
153    private Rect mNextAppTransitionInsets = new Rect();
154
155    private Rect mTmpFromClipRect = new Rect();
156    private Rect mTmpToClipRect = new Rect();
157
158    private final static int APP_STATE_IDLE = 0;
159    private final static int APP_STATE_READY = 1;
160    private final static int APP_STATE_RUNNING = 2;
161    private final static int APP_STATE_TIMEOUT = 3;
162    private int mAppTransitionState = APP_STATE_IDLE;
163
164    private final int mConfigShortAnimTime;
165    private final Interpolator mDecelerateInterpolator;
166    private final Interpolator mThumbnailFadeInInterpolator;
167    private final Interpolator mThumbnailFadeOutInterpolator;
168    private final Interpolator mThumbnailFastOutSlowInInterpolator;
169
170    private int mCurrentUserId = 0;
171
172    AppTransition(Context context, Handler h) {
173        mContext = context;
174        mH = h;
175        mConfigShortAnimTime = context.getResources().getInteger(
176                com.android.internal.R.integer.config_shortAnimTime);
177        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
178                com.android.internal.R.interpolator.decelerate_cubic);
179        mThumbnailFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
180                com.android.internal.R.interpolator.fast_out_slow_in);
181        mThumbnailFadeInInterpolator = new Interpolator() {
182            @Override
183            public float getInterpolation(float input) {
184                // Linear response for first fraction, then complete after that.
185                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
186                    return 0f;
187                }
188                return (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
189                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
190            }
191        };
192        mThumbnailFadeOutInterpolator = new Interpolator() {
193            @Override
194            public float getInterpolation(float input) {
195                // Linear response for first fraction, then complete after that.
196                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
197                    return input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
198                }
199                return 1f;
200            }
201        };
202    }
203
204    boolean isTransitionSet() {
205        return mNextAppTransition != TRANSIT_UNSET;
206    }
207
208    boolean isTransitionNone() {
209        return mNextAppTransition == TRANSIT_NONE;
210    }
211
212    boolean isTransitionEqual(int transit) {
213        return mNextAppTransition == transit;
214    }
215
216    int getAppTransition() {
217        return mNextAppTransition;
218     }
219
220    void setAppTransition(int transit) {
221        mNextAppTransition = transit;
222    }
223
224    boolean isReady() {
225        return mAppTransitionState == APP_STATE_READY
226                || mAppTransitionState == APP_STATE_TIMEOUT;
227    }
228
229    void setReady() {
230        mAppTransitionState = APP_STATE_READY;
231    }
232
233    boolean isRunning() {
234        return mAppTransitionState == APP_STATE_RUNNING;
235    }
236
237    void setIdle() {
238        mAppTransitionState = APP_STATE_IDLE;
239    }
240
241    boolean isTimeout() {
242        return mAppTransitionState == APP_STATE_TIMEOUT;
243    }
244
245    void setTimeout() {
246        mAppTransitionState = APP_STATE_TIMEOUT;
247    }
248
249    Bitmap getNextAppTransitionThumbnail() {
250        return mNextAppTransitionThumbnail;
251    }
252
253    /** Returns whether the next thumbnail transition is aspect scaled up. */
254    boolean isNextThumbnailTransitionAspectScaled() {
255        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
256                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
257    }
258
259    /** Returns whether the next thumbnail transition is scaling up. */
260    boolean isNextThumbnailTransitionScaleUp() {
261        return mNextAppTransitionScaleUp;
262    }
263
264    int getStartingX() {
265        return mNextAppTransitionStartX;
266    }
267
268    int getStartingY() {
269        return mNextAppTransitionStartY;
270    }
271
272    void prepare() {
273        if (!isRunning()) {
274            mAppTransitionState = APP_STATE_IDLE;
275        }
276    }
277
278    void goodToGo() {
279        mNextAppTransition = TRANSIT_UNSET;
280        mAppTransitionState = APP_STATE_RUNNING;
281    }
282
283    void clear() {
284        mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
285        mNextAppTransitionPackage = null;
286        mNextAppTransitionThumbnail = null;
287    }
288
289    void freeze() {
290        setAppTransition(AppTransition.TRANSIT_UNSET);
291        clear();
292        setReady();
293    }
294
295    private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
296        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
297                + (lp != null ? lp.packageName : null)
298                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
299        if (lp != null && lp.windowAnimations != 0) {
300            // If this is a system resource, don't try to load it from the
301            // application resources.  It is nice to avoid loading application
302            // resources if we can.
303            String packageName = lp.packageName != null ? lp.packageName : "android";
304            int resId = lp.windowAnimations;
305            if ((resId&0xFF000000) == 0x01000000) {
306                packageName = "android";
307            }
308            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
309                    + packageName);
310            return AttributeCache.instance().get(packageName, resId,
311                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
312        }
313        return null;
314    }
315
316    private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
317        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
318                + packageName + " resId=0x" + Integer.toHexString(resId));
319        if (packageName != null) {
320            if ((resId&0xFF000000) == 0x01000000) {
321                packageName = "android";
322            }
323            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
324                    + packageName);
325            return AttributeCache.instance().get(packageName, resId,
326                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
327        }
328        return null;
329    }
330
331    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
332        int anim = 0;
333        Context context = mContext;
334        if (animAttr >= 0) {
335            AttributeCache.Entry ent = getCachedAnimations(lp);
336            if (ent != null) {
337                context = ent.context;
338                anim = ent.array.getResourceId(animAttr, 0);
339            }
340        }
341        if (anim != 0) {
342            return AnimationUtils.loadAnimation(context, anim);
343        }
344        return null;
345    }
346
347    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
348        Context context = mContext;
349        if (resId >= 0) {
350            AttributeCache.Entry ent = getCachedAnimations(lp);
351            if (ent != null) {
352                context = ent.context;
353            }
354            return AnimationUtils.loadAnimation(context, resId);
355        }
356        return null;
357    }
358
359    private Animation loadAnimationRes(String packageName, int resId) {
360        int anim = 0;
361        Context context = mContext;
362        if (resId >= 0) {
363            AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
364            if (ent != null) {
365                context = ent.context;
366                anim = resId;
367            }
368        }
369        if (anim != 0) {
370            return AnimationUtils.loadAnimation(context, anim);
371        }
372        return null;
373    }
374
375    /**
376     * Compute the pivot point for an animation that is scaling from a small
377     * rect on screen to a larger rect.  The pivot point varies depending on
378     * the distance between the inner and outer edges on both sides.  This
379     * function computes the pivot point for one dimension.
380     * @param startPos  Offset from left/top edge of outer rectangle to
381     * left/top edge of inner rectangle.
382     * @param finalScale The scaling factor between the size of the outer
383     * and inner rectangles.
384     */
385    private static float computePivot(int startPos, float finalScale) {
386        final float denom = finalScale-1;
387        if (Math.abs(denom) < .0001f) {
388            return startPos;
389        }
390        return -startPos / denom;
391    }
392
393    private Animation createScaleUpAnimationLocked(int transit, boolean enter,
394                                                   int appWidth, int appHeight) {
395        Animation a = null;
396        if (enter) {
397            // Entering app zooms out from the center of the initial rect.
398            float scaleW = mNextAppTransitionStartWidth / (float) appWidth;
399            float scaleH = mNextAppTransitionStartHeight / (float) appHeight;
400            Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
401                    computePivot(mNextAppTransitionStartX, scaleW),
402                    computePivot(mNextAppTransitionStartY, scaleH));
403            scale.setInterpolator(mDecelerateInterpolator);
404
405            Animation alpha = new AlphaAnimation(0, 1);
406            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
407
408            AnimationSet set = new AnimationSet(false);
409            set.addAnimation(scale);
410            set.addAnimation(alpha);
411            set.setDetachWallpaper(true);
412            a = set;
413        } else  if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
414                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
415            // If we are on top of the wallpaper, we need an animation that
416            // correctly handles the wallpaper staying static behind all of
417            // the animated elements.  To do this, will just have the existing
418            // element fade out.
419            a = new AlphaAnimation(1, 0);
420            a.setDetachWallpaper(true);
421        } else {
422            // For normal animations, the exiting element just holds in place.
423            a = new AlphaAnimation(1, 1);
424        }
425
426        // Pick the desired duration.  If this is an inter-activity transition,
427        // it  is the standard duration for that.  Otherwise we use the longer
428        // task transition duration.
429        final long duration;
430        switch (transit) {
431            case TRANSIT_ACTIVITY_OPEN:
432            case TRANSIT_ACTIVITY_CLOSE:
433                duration = mConfigShortAnimTime;
434                break;
435            default:
436                duration = DEFAULT_APP_TRANSITION_DURATION;
437                break;
438        }
439        a.setDuration(duration);
440        a.setFillAfter(true);
441        a.setInterpolator(mDecelerateInterpolator);
442        a.initialize(appWidth, appHeight, appWidth, appHeight);
443        return a;
444    }
445
446    /**
447     * Prepares the specified animation with a standard duration, interpolator, etc.
448     */
449    Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
450            int duration, Interpolator interpolator) {
451        if (duration > 0) {
452            a.setDuration(duration);
453        }
454        a.setFillAfter(true);
455        a.setInterpolator(interpolator);
456        a.initialize(appWidth, appHeight, appWidth, appHeight);
457        return a;
458    }
459
460    /**
461     * Prepares the specified animation with a standard duration, interpolator, etc.
462     */
463    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
464        // Pick the desired duration.  If this is an inter-activity transition,
465        // it  is the standard duration for that.  Otherwise we use the longer
466        // task transition duration.
467        final int duration;
468        switch (transit) {
469            case TRANSIT_ACTIVITY_OPEN:
470            case TRANSIT_ACTIVITY_CLOSE:
471                duration = mConfigShortAnimTime;
472                break;
473            default:
474                duration = DEFAULT_APP_TRANSITION_DURATION;
475                break;
476        }
477        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
478                mDecelerateInterpolator);
479    }
480
481    /**
482     * Return the current thumbnail transition state.
483     */
484    int getThumbnailTransitionState(boolean enter) {
485        if (enter) {
486            if (mNextAppTransitionScaleUp) {
487                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
488            } else {
489                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
490            }
491        } else {
492            if (mNextAppTransitionScaleUp) {
493                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
494            } else {
495                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
496            }
497        }
498    }
499
500    /**
501     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
502     * when a thumbnail is specified with the activity options.
503     */
504    Animation createThumbnailAspectScaleAnimationLocked(int appWidth, int appHeight,
505            int deviceWidth, int transit) {
506        Animation a;
507        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
508        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
509        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
510        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
511
512        float scaleW = deviceWidth / thumbWidth;
513        float unscaledWidth = deviceWidth;
514        float unscaledHeight = thumbHeight * scaleW;
515        float unscaledStartY = mNextAppTransitionStartY - (unscaledHeight - thumbHeight) / 2f;
516        if (mNextAppTransitionScaleUp) {
517            // Animation up from the thumbnail to the full screen
518            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
519                    mNextAppTransitionStartX + (thumbWidth / 2f),
520                    mNextAppTransitionStartY + (thumbHeight / 2f));
521            scale.setInterpolator(mThumbnailFastOutSlowInInterpolator);
522            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
523            Animation alpha = new AlphaAnimation(1, 0);
524            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
525            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
526            Animation translate = new TranslateAnimation(0, 0, 0, -unscaledStartY +
527                    mNextAppTransitionInsets.top);
528            translate.setInterpolator(mThumbnailFastOutSlowInInterpolator);
529            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
530
531            // This AnimationSet uses the Interpolators assigned above.
532            AnimationSet set = new AnimationSet(false);
533            set.addAnimation(scale);
534            set.addAnimation(alpha);
535            set.addAnimation(translate);
536            a = set;
537        } else {
538            // Animation down from the full screen to the thumbnail
539            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
540                    mNextAppTransitionStartX + (thumbWidth / 2f),
541                    mNextAppTransitionStartY + (thumbHeight / 2f));
542            scale.setInterpolator(mThumbnailFastOutSlowInInterpolator);
543            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
544            Animation alpha = new AlphaAnimation(0f, 1f);
545            alpha.setInterpolator(mThumbnailFadeInInterpolator);
546            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
547            Animation translate = new TranslateAnimation(0, 0, -unscaledStartY +
548                    mNextAppTransitionInsets.top, 0);
549            translate.setInterpolator(mThumbnailFastOutSlowInInterpolator);
550            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
551
552            // This AnimationSet uses the Interpolators assigned above.
553            AnimationSet set = new AnimationSet(false);
554            set.addAnimation(scale);
555            set.addAnimation(alpha);
556            set.addAnimation(translate);
557            a = set;
558
559        }
560        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, 0,
561                mThumbnailFastOutSlowInInterpolator);
562    }
563
564    /**
565     * This alternate animation is created when we are doing a thumbnail transition, for the
566     * activity that is leaving, and the activity that is entering.
567     */
568    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
569            int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
570            Rect contentInsets, boolean isFullScreen) {
571        Animation a;
572        final int thumbWidthI = mNextAppTransitionStartWidth;
573        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
574        final int thumbHeightI = mNextAppTransitionStartHeight;
575        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
576
577        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
578        float scale = 1f;
579        int scaledTopDecor = 0;
580
581        switch (thumbTransitState) {
582            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
583                // App window scaling up to become full screen
584                if (orientation == Configuration.ORIENTATION_PORTRAIT) {
585                    // In portrait, we scale the width and clip to the top/left square
586                    scale = thumbWidth / appWidth;
587                    scaledTopDecor = (int) (scale * contentInsets.top);
588                    int unscaledThumbHeight = (int) (thumbHeight / scale);
589                    mTmpFromClipRect.set(containingFrame);
590                    if (isFullScreen) {
591                        mTmpFromClipRect.top = contentInsets.top;
592                    }
593                    mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
594                    mTmpToClipRect.set(containingFrame);
595                } else {
596                    // In landscape, we scale the height and clip to the top/left square
597                    scale = thumbHeight / (appHeight - contentInsets.top);
598                    scaledTopDecor = (int) (scale * contentInsets.top);
599                    int unscaledThumbWidth = (int) (thumbWidth / scale);
600                    int unscaledThumbHeight = (int) (thumbHeight / scale);
601                    mTmpFromClipRect.set(containingFrame);
602                    if (isFullScreen) {
603                        mTmpFromClipRect.top = contentInsets.top;
604                        mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
605                    }
606                    mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
607                    mTmpToClipRect.set(containingFrame);
608                }
609                mNextAppTransitionInsets.set(contentInsets);
610
611                Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
612                        computePivot(mNextAppTransitionStartX, scale),
613                        computePivot(mNextAppTransitionStartY, scale));
614                Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
615                Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
616
617                AnimationSet set = new AnimationSet(true);
618                set.addAnimation(clipAnim);
619                set.addAnimation(scaleAnim);
620                set.addAnimation(translateAnim);
621                a = set;
622                break;
623            }
624            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
625                // Previous app window during the scale up
626                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
627                    // Fade out the source activity if we are animating to a wallpaper
628                    // activity.
629                    a = new AlphaAnimation(1, 0);
630                } else {
631                    a = new AlphaAnimation(1, 1);
632                }
633                break;
634            }
635            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
636                // Target app window during the scale down
637                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
638                    // Fade in the destination activity if we are animating from a wallpaper
639                    // activity.
640                    a = new AlphaAnimation(0, 1);
641                } else {
642                    a = new AlphaAnimation(1, 1);
643                }
644                break;
645            }
646            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
647                // App window scaling down from full screen
648                if (orientation == Configuration.ORIENTATION_PORTRAIT) {
649                    // In portrait, we scale the width and clip to the top/left square
650                    scale = thumbWidth / appWidth;
651                    scaledTopDecor = (int) (scale * contentInsets.top);
652                    int unscaledThumbHeight = (int) (thumbHeight / scale);
653                    mTmpFromClipRect.set(containingFrame);
654                    mTmpToClipRect.set(containingFrame);
655                    if (isFullScreen) {
656                        mTmpToClipRect.top = contentInsets.top;
657                    }
658                    mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
659                } else {
660                    // In landscape, we scale the height and clip to the top/left square
661                    scale = thumbHeight / (appHeight - contentInsets.top);
662                    scaledTopDecor = (int) (scale * contentInsets.top);
663                    int unscaledThumbWidth = (int) (thumbWidth / scale);
664                    int unscaledThumbHeight = (int) (thumbHeight / scale);
665                    mTmpFromClipRect.set(containingFrame);
666                    mTmpToClipRect.set(containingFrame);
667                    if (isFullScreen) {
668                        mTmpToClipRect.top = contentInsets.top;
669                        mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
670                    }
671                    mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
672                }
673                mNextAppTransitionInsets.set(contentInsets);
674
675                Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
676                        computePivot(mNextAppTransitionStartX, scale),
677                        computePivot(mNextAppTransitionStartY, scale));
678                Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
679                Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
680
681                AnimationSet set = new AnimationSet(true);
682                set.addAnimation(clipAnim);
683                set.addAnimation(scaleAnim);
684                set.addAnimation(translateAnim);
685
686                a = set;
687                a.setZAdjustment(Animation.ZORDER_TOP);
688                break;
689            }
690            default:
691                throw new RuntimeException("Invalid thumbnail transition state");
692        }
693
694        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
695                THUMBNAIL_APP_TRANSITION_DURATION);
696        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
697                mThumbnailFastOutSlowInInterpolator);
698    }
699
700    /**
701     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
702     * when a thumbnail is specified with the activity options.
703     */
704    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
705        Animation a;
706        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
707        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
708        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
709        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
710
711        if (mNextAppTransitionScaleUp) {
712            // Animation for the thumbnail zooming from its initial size to the full screen
713            float scaleW = appWidth / thumbWidth;
714            float scaleH = appHeight / thumbHeight;
715            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
716                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
717                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
718            scale.setInterpolator(mDecelerateInterpolator);
719
720            Animation alpha = new AlphaAnimation(1, 0);
721            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
722
723            // This AnimationSet uses the Interpolators assigned above.
724            AnimationSet set = new AnimationSet(false);
725            set.addAnimation(scale);
726            set.addAnimation(alpha);
727            a = set;
728        } else {
729            // Animation for the thumbnail zooming down from the full screen to its final size
730            float scaleW = appWidth / thumbWidth;
731            float scaleH = appHeight / thumbHeight;
732            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
733                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
734                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
735        }
736
737        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
738    }
739
740    /**
741     * This animation is created when we are doing a thumbnail transition, for the activity that is
742     * leaving, and the activity that is entering.
743     */
744    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
745                                                    int appHeight, int transit) {
746        Animation a;
747        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
748        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
749        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
750        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
751
752        switch (thumbTransitState) {
753            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
754                // Entering app scales up with the thumbnail
755                float scaleW = thumbWidth / appWidth;
756                float scaleH = thumbHeight / appHeight;
757                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
758                        computePivot(mNextAppTransitionStartX, scaleW),
759                        computePivot(mNextAppTransitionStartY, scaleH));
760                break;
761            }
762            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
763                // Exiting app while the thumbnail is scaling up should fade or stay in place
764                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
765                    // Fade out while bringing up selected activity. This keeps the
766                    // current activity from showing through a launching wallpaper
767                    // activity.
768                    a = new AlphaAnimation(1, 0);
769                } else {
770                    // noop animation
771                    a = new AlphaAnimation(1, 1);
772                }
773                break;
774            }
775            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
776                // Entering the other app, it should just be visible while we scale the thumbnail
777                // down above it
778                a = new AlphaAnimation(1, 1);
779                break;
780            }
781            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
782                // Exiting the current app, the app should scale down with the thumbnail
783                float scaleW = thumbWidth / appWidth;
784                float scaleH = thumbHeight / appHeight;
785                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
786                        computePivot(mNextAppTransitionStartX, scaleW),
787                        computePivot(mNextAppTransitionStartY, scaleH));
788
789                Animation alpha = new AlphaAnimation(1, 0);
790
791                AnimationSet set = new AnimationSet(true);
792                set.addAnimation(scale);
793                set.addAnimation(alpha);
794                set.setZAdjustment(Animation.ZORDER_TOP);
795                a = set;
796                break;
797            }
798            default:
799                throw new RuntimeException("Invalid thumbnail transition state");
800        }
801
802        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
803    }
804
805
806    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
807            int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
808            boolean isFullScreen, boolean isVoiceInteraction) {
809        Animation a;
810        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
811                || transit == TRANSIT_TASK_OPEN
812                || transit == TRANSIT_TASK_TO_FRONT)) {
813            a = loadAnimationRes(lp, enter
814                    ? com.android.internal.R.anim.voice_activity_open_enter
815                    : com.android.internal.R.anim.voice_activity_open_exit);
816            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
817                    "applyAnimation voice:"
818                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
819                    + " Callers=" + Debug.getCallers(3));
820        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
821                || transit == TRANSIT_TASK_CLOSE
822                || transit == TRANSIT_TASK_TO_BACK)) {
823            a = loadAnimationRes(lp, enter
824                    ? com.android.internal.R.anim.voice_activity_close_enter
825                    : com.android.internal.R.anim.voice_activity_close_exit);
826            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
827                    "applyAnimation voice:"
828                    + " anim=" + a + " transit=" + transit + " isEntrance=" + enter
829                    + " Callers=" + Debug.getCallers(3));
830        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
831            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
832                    mNextAppTransitionEnter : mNextAppTransitionExit);
833            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
834                    "applyAnimation:"
835                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
836                    + " transit=" + transit + " isEntrance=" + enter
837                    + " Callers=" + Debug.getCallers(3));
838        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
839            a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
840            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
841                    "applyAnimation:"
842                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
843                    + " transit=" + transit + " isEntrance=" + enter
844                    + " Callers=" + Debug.getCallers(3));
845        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
846                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
847            mNextAppTransitionScaleUp =
848                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
849            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
850                    appWidth, appHeight, transit);
851            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
852                String animName = mNextAppTransitionScaleUp ?
853                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
854                Slog.v(TAG, "applyAnimation:"
855                        + " anim=" + a + " nextAppTransition=" + animName
856                        + " transit=" + transit + " isEntrance=" + enter
857                        + " Callers=" + Debug.getCallers(3));
858            }
859        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
860                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
861            mNextAppTransitionScaleUp =
862                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
863            a = createAspectScaledThumbnailEnterExitAnimationLocked(
864                    getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
865                    transit, containingFrame, contentInsets, isFullScreen);
866            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
867                String animName = mNextAppTransitionScaleUp ?
868                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
869                Slog.v(TAG, "applyAnimation:"
870                        + " anim=" + a + " nextAppTransition=" + animName
871                        + " transit=" + transit + " isEntrance=" + enter
872                        + " Callers=" + Debug.getCallers(3));
873            }
874        } else {
875            int animAttr = 0;
876            switch (transit) {
877                case TRANSIT_ACTIVITY_OPEN:
878                    animAttr = enter
879                            ? WindowAnimation_activityOpenEnterAnimation
880                            : WindowAnimation_activityOpenExitAnimation;
881                    break;
882                case TRANSIT_ACTIVITY_CLOSE:
883                    animAttr = enter
884                            ? WindowAnimation_activityCloseEnterAnimation
885                            : WindowAnimation_activityCloseExitAnimation;
886                    break;
887                case TRANSIT_TASK_OPEN:
888                    animAttr = enter
889                            ? WindowAnimation_taskOpenEnterAnimation
890                            : WindowAnimation_taskOpenExitAnimation;
891                    break;
892                case TRANSIT_TASK_CLOSE:
893                    animAttr = enter
894                            ? WindowAnimation_taskCloseEnterAnimation
895                            : WindowAnimation_taskCloseExitAnimation;
896                    break;
897                case TRANSIT_TASK_TO_FRONT:
898                    animAttr = enter
899                            ? WindowAnimation_taskToFrontEnterAnimation
900                            : WindowAnimation_taskToFrontExitAnimation;
901                    break;
902                case TRANSIT_TASK_TO_BACK:
903                    animAttr = enter
904                            ? WindowAnimation_taskToBackEnterAnimation
905                            : WindowAnimation_taskToBackExitAnimation;
906                    break;
907                case TRANSIT_WALLPAPER_OPEN:
908                    animAttr = enter
909                            ? WindowAnimation_wallpaperOpenEnterAnimation
910                            : WindowAnimation_wallpaperOpenExitAnimation;
911                    break;
912                case TRANSIT_WALLPAPER_CLOSE:
913                    animAttr = enter
914                            ? WindowAnimation_wallpaperCloseEnterAnimation
915                            : WindowAnimation_wallpaperCloseExitAnimation;
916                    break;
917                case TRANSIT_WALLPAPER_INTRA_OPEN:
918                    animAttr = enter
919                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
920                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
921                    break;
922                case TRANSIT_WALLPAPER_INTRA_CLOSE:
923                    animAttr = enter
924                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
925                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
926                    break;
927                case TRANSIT_TASK_OPEN_BEHIND:
928                    animAttr = enter
929                            ? WindowAnimation_launchTaskBehindSourceAnimation
930                            : WindowAnimation_launchTaskBehindTargetAnimation;
931            }
932            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
933            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
934                    "applyAnimation:"
935                    + " anim=" + a
936                    + " animAttr=0x" + Integer.toHexString(animAttr)
937                    + " transit=" + transit + " isEntrance=" + enter
938                    + " Callers=" + Debug.getCallers(3));
939        }
940        return a;
941    }
942
943    void postAnimationCallback() {
944        if (mNextAppTransitionCallback != null) {
945            mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
946            mNextAppTransitionCallback = null;
947        }
948    }
949
950    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
951                                             IRemoteCallback startedCallback) {
952        if (isTransitionSet()) {
953            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
954            mNextAppTransitionPackage = packageName;
955            mNextAppTransitionThumbnail = null;
956            mNextAppTransitionEnter = enterAnim;
957            mNextAppTransitionExit = exitAnim;
958            postAnimationCallback();
959            mNextAppTransitionCallback = startedCallback;
960        } else {
961            postAnimationCallback();
962        }
963    }
964
965    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
966                                                    int startHeight) {
967        if (isTransitionSet()) {
968            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
969            mNextAppTransitionPackage = null;
970            mNextAppTransitionThumbnail = null;
971            mNextAppTransitionStartX = startX;
972            mNextAppTransitionStartY = startY;
973            mNextAppTransitionStartWidth = startWidth;
974            mNextAppTransitionStartHeight = startHeight;
975            postAnimationCallback();
976            mNextAppTransitionCallback = null;
977        }
978    }
979
980    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
981                                           IRemoteCallback startedCallback, boolean scaleUp) {
982        if (isTransitionSet()) {
983            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
984                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
985            mNextAppTransitionPackage = null;
986            mNextAppTransitionThumbnail = srcThumb;
987            mNextAppTransitionScaleUp = scaleUp;
988            mNextAppTransitionStartX = startX;
989            mNextAppTransitionStartY = startY;
990            postAnimationCallback();
991            mNextAppTransitionCallback = startedCallback;
992        } else {
993            postAnimationCallback();
994        }
995    }
996
997    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
998            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
999        if (isTransitionSet()) {
1000            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1001                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1002            mNextAppTransitionPackage = null;
1003            mNextAppTransitionThumbnail = srcThumb;
1004            mNextAppTransitionScaleUp = scaleUp;
1005            mNextAppTransitionStartX = startX;
1006            mNextAppTransitionStartY = startY;
1007            mNextAppTransitionStartWidth = targetWidth;
1008            mNextAppTransitionStartHeight = targetHeight;
1009            postAnimationCallback();
1010            mNextAppTransitionCallback = startedCallback;
1011        } else {
1012            postAnimationCallback();
1013        }
1014    }
1015
1016    @Override
1017    public String toString() {
1018        return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition);
1019    }
1020
1021    /**
1022     * Returns the human readable name of a window transition.
1023     *
1024     * @param transition The window transition.
1025     * @return The transition symbolic name.
1026     */
1027    public static String appTransitionToString(int transition) {
1028        switch (transition) {
1029            case TRANSIT_UNSET: {
1030                return "TRANSIT_UNSET";
1031            }
1032            case TRANSIT_NONE: {
1033                return "TRANSIT_NONE";
1034            }
1035            case TRANSIT_ACTIVITY_OPEN: {
1036                return "TRANSIT_ACTIVITY_OPEN";
1037            }
1038            case TRANSIT_ACTIVITY_CLOSE: {
1039                return "TRANSIT_ACTIVITY_CLOSE";
1040            }
1041            case TRANSIT_TASK_OPEN: {
1042                return "TRANSIT_TASK_OPEN";
1043            }
1044            case TRANSIT_TASK_CLOSE: {
1045                return "TRANSIT_TASK_CLOSE";
1046            }
1047            case TRANSIT_TASK_TO_FRONT: {
1048                return "TRANSIT_TASK_TO_FRONT";
1049            }
1050            case TRANSIT_TASK_TO_BACK: {
1051                return "TRANSIT_TASK_TO_BACK";
1052            }
1053            case TRANSIT_WALLPAPER_CLOSE: {
1054                return "TRANSIT_WALLPAPER_CLOSE";
1055            }
1056            case TRANSIT_WALLPAPER_OPEN: {
1057                return "TRANSIT_WALLPAPER_OPEN";
1058            }
1059            case TRANSIT_WALLPAPER_INTRA_OPEN: {
1060                return "TRANSIT_WALLPAPER_INTRA_OPEN";
1061            }
1062            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1063                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1064            }
1065            case TRANSIT_TASK_OPEN_BEHIND: {
1066                return "TRANSIT_TASK_OPEN_BEHIND";
1067            }
1068            default: {
1069                return "<UNKNOWN>";
1070            }
1071        }
1072    }
1073
1074    private String appStateToString() {
1075        switch (mAppTransitionState) {
1076            case APP_STATE_IDLE:
1077                return "APP_STATE_IDLE";
1078            case APP_STATE_READY:
1079                return "APP_STATE_READY";
1080            case APP_STATE_RUNNING:
1081                return "APP_STATE_RUNNING";
1082            case APP_STATE_TIMEOUT:
1083                return "APP_STATE_TIMEOUT";
1084            default:
1085                return "unknown state=" + mAppTransitionState;
1086        }
1087    }
1088
1089    private String transitTypeToString() {
1090        switch (mNextAppTransitionType) {
1091            case NEXT_TRANSIT_TYPE_NONE:
1092                return "NEXT_TRANSIT_TYPE_NONE";
1093            case NEXT_TRANSIT_TYPE_CUSTOM:
1094                return "NEXT_TRANSIT_TYPE_CUSTOM";
1095            case NEXT_TRANSIT_TYPE_SCALE_UP:
1096                return "NEXT_TRANSIT_TYPE_SCALE_UP";
1097            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1098                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1099            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1100                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1101            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1102                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1103            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1104                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1105            default:
1106                return "unknown type=" + mNextAppTransitionType;
1107        }
1108    }
1109
1110    @Override
1111    public void dump(PrintWriter pw) {
1112        pw.print(" " + this);
1113        pw.print("  mAppTransitionState="); pw.println(appStateToString());
1114        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1115            pw.print("  mNextAppTransitionType="); pw.println(transitTypeToString());
1116        }
1117        switch (mNextAppTransitionType) {
1118            case NEXT_TRANSIT_TYPE_CUSTOM:
1119                pw.print("  mNextAppTransitionPackage=");
1120                        pw.println(mNextAppTransitionPackage);
1121                pw.print("  mNextAppTransitionEnter=0x");
1122                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
1123                        pw.print(" mNextAppTransitionExit=0x");
1124                        pw.println(Integer.toHexString(mNextAppTransitionExit));
1125                break;
1126            case NEXT_TRANSIT_TYPE_SCALE_UP:
1127                pw.print("  mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
1128                        pw.print(" mNextAppTransitionStartY=");
1129                        pw.println(mNextAppTransitionStartY);
1130                pw.print("  mNextAppTransitionStartWidth=");
1131                        pw.print(mNextAppTransitionStartWidth);
1132                        pw.print(" mNextAppTransitionStartHeight=");
1133                        pw.println(mNextAppTransitionStartHeight);
1134                break;
1135            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1136            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1137            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1138            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1139                pw.print("  mNextAppTransitionThumbnail=");
1140                        pw.print(mNextAppTransitionThumbnail);
1141                        pw.print(" mNextAppTransitionStartX=");
1142                        pw.print(mNextAppTransitionStartX);
1143                        pw.print(" mNextAppTransitionStartY=");
1144                        pw.println(mNextAppTransitionStartY);
1145                        pw.print(" mNextAppTransitionStartWidth=");
1146                        pw.print(mNextAppTransitionStartWidth);
1147                        pw.print(" mNextAppTransitionStartHeight=");
1148                        pw.println(mNextAppTransitionStartHeight);
1149                pw.print("  mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
1150                break;
1151        }
1152        if (mNextAppTransitionCallback != null) {
1153            pw.print("  mNextAppTransitionCallback=");
1154            pw.println(mNextAppTransitionCallback);
1155        }
1156    }
1157
1158    public void setCurrentUser(int newUserId) {
1159        mCurrentUserId = newUserId;
1160    }
1161}
1162