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