AppTransition.java revision fd08622588d5a90c11216894344e58147ff6359a
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.WindowManager;
30import android.view.animation.AlphaAnimation;
31import android.view.animation.Animation;
32import android.view.animation.AnimationSet;
33import android.view.animation.AnimationUtils;
34import android.view.animation.ClipRectAnimation;
35import android.view.animation.Interpolator;
36import android.view.animation.ScaleAnimation;
37
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        Animation a;
505        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
506        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
507        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
508        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
509
510        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
511        float scale = 1f;
512        int scaledTopDecor = 0;
513
514        switch (thumbTransitState) {
515            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
516                // Entering app scales up with the thumbnail
517                if (orientation == Configuration.ORIENTATION_PORTRAIT) {
518                    // In portrait, we scale the width and clip to the top/left square
519                    scale = thumbWidth / appWidth;
520                    scaledTopDecor = (int) (scale * contentInsets.top);
521                    int unscaledThumbHeight = (int) (thumbHeight / scale);
522                    mTmpFromClipRect.set(containingFrame);
523                    mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
524                    mTmpToClipRect.set(containingFrame);
525                } else {
526                    // In landscape, we scale the height and clip to the top/left square
527                    scale = thumbHeight / (appHeight - contentInsets.top);
528                    scaledTopDecor = (int) (scale * contentInsets.top);
529                    int unscaledThumbWidth = (int) (thumbWidth / scale);
530                    mTmpFromClipRect.set(containingFrame);
531                    mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
532                    mTmpToClipRect.set(containingFrame);
533                }
534
535                Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
536                        computePivot(mNextAppTransitionStartX, scale),
537                        computePivot(mNextAppTransitionStartY, scale));
538                Animation alphaAnim = new AlphaAnimation(1, 1);
539                Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
540                Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
541
542                AnimationSet set = new AnimationSet(true);
543                set.addAnimation(alphaAnim);
544                set.addAnimation(clipAnim);
545                set.addAnimation(scaleAnim);
546                set.addAnimation(translateAnim);
547                a = set;
548                break;
549            }
550            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
551                // Exiting app while the thumbnail is scaling up should fade
552                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
553                    // Fade out while bringing up selected activity. This keeps the
554                    // current activity from showing through a launching wallpaper
555                    // activity.
556                    a = new AlphaAnimation(1, 0);
557                } else {
558                    // noop animation
559                    a = new AlphaAnimation(1, 1);
560                }
561                break;
562            }
563            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
564                // Entering the other app, it should just be visible while we scale the thumbnail
565                // down above it
566                a = new AlphaAnimation(1, 1);
567                break;
568            }
569            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
570                // Exiting the current app, the app should scale down with the thumbnail
571                if (orientation == Configuration.ORIENTATION_PORTRAIT) {
572                    // In portrait, we scale the width and clip to the top/left square
573                    scale = thumbWidth / appWidth;
574                    scaledTopDecor = (int) (scale * contentInsets.top);
575                    int unscaledThumbHeight = (int) (thumbHeight / scale);
576                    mTmpFromClipRect.set(containingFrame);
577                    mTmpToClipRect.set(containingFrame);
578                    mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
579                } else {
580                    // In landscape, we scale the height and clip to the top/left square
581                    scale = thumbHeight / (appHeight - contentInsets.top);
582                    scaledTopDecor = (int) (scale * contentInsets.top);
583                    int unscaledThumbWidth = (int) (thumbWidth / scale);
584                    mTmpFromClipRect.set(containingFrame);
585                    mTmpToClipRect.set(containingFrame);
586                    mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
587                }
588
589                Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
590                        computePivot(mNextAppTransitionStartX, scale),
591                        computePivot(mNextAppTransitionStartY, scale));
592                Animation alphaAnim = new AlphaAnimation(1, 1);
593                Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
594                Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
595
596                AnimationSet set = new AnimationSet(true);
597                set.addAnimation(alphaAnim);
598                set.addAnimation(clipAnim);
599                set.addAnimation(scaleAnim);
600                set.addAnimation(translateAnim);
601
602                a = set;
603                a.setZAdjustment(Animation.ZORDER_TOP);
604                break;
605            }
606            default:
607                throw new RuntimeException("Invalid thumbnail transition state");
608        }
609
610        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
611                THUMBNAIL_APP_TRANSITION_DURATION, mThumbnailCubicInterpolator);
612    }
613
614    /**
615     * This animation is created when we are doing a thumbnail transition, for the activity that is
616     * leaving, and the activity that is entering.
617     */
618    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
619                                                    int appHeight, int transit) {
620        Animation a;
621        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
622        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
623        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
624        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
625
626        switch (thumbTransitState) {
627            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
628                // Entering app scales up with the thumbnail
629                float scaleW = thumbWidth / appWidth;
630                float scaleH = thumbHeight / appHeight;
631                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
632                        computePivot(mNextAppTransitionStartX, scaleW),
633                        computePivot(mNextAppTransitionStartY, scaleH));
634                break;
635            }
636            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
637                // Exiting app while the thumbnail is scaling up should fade or stay in place
638                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
639                    // Fade out while bringing up selected activity. This keeps the
640                    // current activity from showing through a launching wallpaper
641                    // activity.
642                    a = new AlphaAnimation(1, 0);
643                } else {
644                    // noop animation
645                    a = new AlphaAnimation(1, 1);
646                }
647                break;
648            }
649            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
650                // Entering the other app, it should just be visible while we scale the thumbnail
651                // down above it
652                a = new AlphaAnimation(1, 1);
653                break;
654            }
655            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
656                // Exiting the current app, the app should scale down with the thumbnail
657                float scaleW = thumbWidth / appWidth;
658                float scaleH = thumbHeight / appHeight;
659                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
660                        computePivot(mNextAppTransitionStartX, scaleW),
661                        computePivot(mNextAppTransitionStartY, scaleH));
662
663                Animation alpha = new AlphaAnimation(1, 0);
664
665                AnimationSet set = new AnimationSet(true);
666                set.addAnimation(scale);
667                set.addAnimation(alpha);
668                set.setZAdjustment(Animation.ZORDER_TOP);
669                a = set;
670                break;
671            }
672            default:
673                throw new RuntimeException("Invalid thumbnail transition state");
674        }
675
676        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
677    }
678
679
680    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
681                            int appWidth, int appHeight, int orientation,
682                            Rect containingFrame, Rect contentInsets) {
683        Animation a;
684        if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
685            a = loadAnimation(mNextAppTransitionPackage, enter ?
686                    mNextAppTransitionEnter : mNextAppTransitionExit);
687            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
688                    "applyAnimation:"
689                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
690                    + " transit=" + transit + " isEntrance=" + enter
691                    + " Callers=" + Debug.getCallers(3));
692        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
693            a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
694            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
695                    "applyAnimation:"
696                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
697                    + " transit=" + transit + " isEntrance=" + enter
698                    + " Callers=" + Debug.getCallers(3));
699        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
700                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
701            mNextAppTransitionScaleUp =
702                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
703            a = createAlternateThumbnailEnterExitAnimationLocked(
704                    getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
705                    transit, containingFrame, contentInsets);
706            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
707                String animName = mNextAppTransitionScaleUp ?
708                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
709                Slog.v(TAG, "applyAnimation:"
710                        + " anim=" + a + " nextAppTransition=" + animName
711                        + " transit=" + transit + " isEntrance=" + enter
712                        + " Callers=" + Debug.getCallers(3));
713            }
714        } else {
715            int animAttr = 0;
716            switch (transit) {
717                case TRANSIT_ACTIVITY_OPEN:
718                    animAttr = enter
719                            ? WindowAnimation_activityOpenEnterAnimation
720                            : WindowAnimation_activityOpenExitAnimation;
721                    break;
722                case TRANSIT_ACTIVITY_CLOSE:
723                    animAttr = enter
724                            ? WindowAnimation_activityCloseEnterAnimation
725                            : WindowAnimation_activityCloseExitAnimation;
726                    break;
727                case TRANSIT_TASK_OPEN:
728                    animAttr = enter
729                            ? WindowAnimation_taskOpenEnterAnimation
730                            : WindowAnimation_taskOpenExitAnimation;
731                    break;
732                case TRANSIT_TASK_CLOSE:
733                    animAttr = enter
734                            ? WindowAnimation_taskCloseEnterAnimation
735                            : WindowAnimation_taskCloseExitAnimation;
736                    break;
737                case TRANSIT_TASK_TO_FRONT:
738                    animAttr = enter
739                            ? WindowAnimation_taskToFrontEnterAnimation
740                            : WindowAnimation_taskToFrontExitAnimation;
741                    break;
742                case TRANSIT_TASK_TO_BACK:
743                    animAttr = enter
744                            ? WindowAnimation_taskToBackEnterAnimation
745                            : WindowAnimation_taskToBackExitAnimation;
746                    break;
747                case TRANSIT_WALLPAPER_OPEN:
748                    animAttr = enter
749                            ? WindowAnimation_wallpaperOpenEnterAnimation
750                            : WindowAnimation_wallpaperOpenExitAnimation;
751                    break;
752                case TRANSIT_WALLPAPER_CLOSE:
753                    animAttr = enter
754                            ? WindowAnimation_wallpaperCloseEnterAnimation
755                            : WindowAnimation_wallpaperCloseExitAnimation;
756                    break;
757                case TRANSIT_WALLPAPER_INTRA_OPEN:
758                    animAttr = enter
759                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
760                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
761                    break;
762                case TRANSIT_WALLPAPER_INTRA_CLOSE:
763                    animAttr = enter
764                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
765                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
766                    break;
767            }
768            a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
769            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
770                    "applyAnimation:"
771                    + " anim=" + a
772                    + " animAttr=0x" + Integer.toHexString(animAttr)
773                    + " transit=" + transit + " isEntrance=" + enter
774                    + " Callers=" + Debug.getCallers(3));
775        }
776        return a;
777    }
778
779    void postAnimationCallback() {
780        if (mNextAppTransitionCallback != null) {
781            mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
782            mNextAppTransitionCallback = null;
783        }
784    }
785
786    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
787                                             IRemoteCallback startedCallback) {
788        if (isTransitionSet()) {
789            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
790            mNextAppTransitionPackage = packageName;
791            mNextAppTransitionThumbnail = null;
792            mNextAppTransitionEnter = enterAnim;
793            mNextAppTransitionExit = exitAnim;
794            postAnimationCallback();
795            mNextAppTransitionCallback = startedCallback;
796        } else {
797            postAnimationCallback();
798        }
799    }
800
801    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
802                                                    int startHeight) {
803        if (isTransitionSet()) {
804            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
805            mNextAppTransitionPackage = null;
806            mNextAppTransitionThumbnail = null;
807            mNextAppTransitionStartX = startX;
808            mNextAppTransitionStartY = startY;
809            mNextAppTransitionStartWidth = startWidth;
810            mNextAppTransitionStartHeight = startHeight;
811            postAnimationCallback();
812            mNextAppTransitionCallback = null;
813        }
814    }
815
816    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
817                                           IRemoteCallback startedCallback, boolean scaleUp) {
818        if (isTransitionSet()) {
819            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
820                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
821            mNextAppTransitionPackage = null;
822            mNextAppTransitionThumbnail = srcThumb;
823            mNextAppTransitionScaleUp = scaleUp;
824            mNextAppTransitionStartX = startX;
825            mNextAppTransitionStartY = startY;
826            postAnimationCallback();
827            mNextAppTransitionCallback = startedCallback;
828        } else {
829            postAnimationCallback();
830        }
831    }
832
833    @Override
834    public String toString() {
835        return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition);
836    }
837
838    /**
839     * Returns the human readable name of a window transition.
840     *
841     * @param transition The window transition.
842     * @return The transition symbolic name.
843     */
844    public static String appTransitionToString(int transition) {
845        switch (transition) {
846            case TRANSIT_UNSET: {
847                return "TRANSIT_UNSET";
848            }
849            case TRANSIT_NONE: {
850                return "TRANSIT_NONE";
851            }
852            case TRANSIT_EXIT_MASK: {
853                return "TRANSIT_EXIT_MASK";
854            }
855            case TRANSIT_ACTIVITY_OPEN: {
856                return "TRANSIT_ACTIVITY_OPEN";
857            }
858            case TRANSIT_ACTIVITY_CLOSE: {
859                return "TRANSIT_ACTIVITY_CLOSE";
860            }
861            case TRANSIT_TASK_OPEN: {
862                return "TRANSIT_TASK_OPEN";
863            }
864            case TRANSIT_TASK_CLOSE: {
865                return "TRANSIT_TASK_CLOSE";
866            }
867            case TRANSIT_TASK_TO_FRONT: {
868                return "TRANSIT_TASK_TO_FRONT";
869            }
870            case TRANSIT_TASK_TO_BACK: {
871                return "TRANSIT_TASK_TO_BACK";
872            }
873            case TRANSIT_WALLPAPER_CLOSE: {
874                return "TRANSIT_WALLPAPER_CLOSE";
875            }
876            case TRANSIT_WALLPAPER_OPEN: {
877                return "TRANSIT_WALLPAPER_OPEN";
878            }
879            case TRANSIT_WALLPAPER_INTRA_OPEN: {
880                return "TRANSIT_WALLPAPER_INTRA_OPEN";
881            }
882            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
883                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
884            }
885            default: {
886                return "<UNKNOWN>";
887            }
888        }
889    }
890
891    private String appStateToString() {
892        switch (mAppTransitionState) {
893            case APP_STATE_IDLE:
894                return "APP_STATE_IDLE";
895            case APP_STATE_READY:
896                return "APP_STATE_READY";
897            case APP_STATE_RUNNING:
898                return "APP_STATE_RUNNING";
899            case APP_STATE_TIMEOUT:
900                return "APP_STATE_TIMEOUT";
901            default:
902                return "unknown state=" + mAppTransitionState;
903        }
904    }
905
906    private String transitTypeToString() {
907        switch (mNextAppTransitionType) {
908            case NEXT_TRANSIT_TYPE_NONE:
909                return "NEXT_TRANSIT_TYPE_NONE";
910            case NEXT_TRANSIT_TYPE_CUSTOM:
911                return "NEXT_TRANSIT_TYPE_CUSTOM";
912            case NEXT_TRANSIT_TYPE_SCALE_UP:
913                return "NEXT_TRANSIT_TYPE_SCALE_UP";
914            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
915                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
916            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
917                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
918            default:
919                return "unknown type=" + mNextAppTransitionType;
920        }
921    }
922
923    @Override
924    public void dump(PrintWriter pw) {
925        pw.print(" " + this);
926        pw.print("  mAppTransitionState="); pw.println(appStateToString());
927        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
928            pw.print("  mNextAppTransitionType="); pw.println(transitTypeToString());
929        }
930        switch (mNextAppTransitionType) {
931            case NEXT_TRANSIT_TYPE_CUSTOM:
932                pw.print("  mNextAppTransitionPackage=");
933                        pw.println(mNextAppTransitionPackage);
934                pw.print("  mNextAppTransitionEnter=0x");
935                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
936                        pw.print(" mNextAppTransitionExit=0x");
937                        pw.println(Integer.toHexString(mNextAppTransitionExit));
938                break;
939            case NEXT_TRANSIT_TYPE_SCALE_UP:
940                pw.print("  mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
941                        pw.print(" mNextAppTransitionStartY=");
942                        pw.println(mNextAppTransitionStartY);
943                pw.print("  mNextAppTransitionStartWidth=");
944                        pw.print(mNextAppTransitionStartWidth);
945                        pw.print(" mNextAppTransitionStartHeight=");
946                        pw.println(mNextAppTransitionStartHeight);
947                break;
948            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
949            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
950                pw.print("  mNextAppTransitionThumbnail=");
951                        pw.print(mNextAppTransitionThumbnail);
952                        pw.print(" mNextAppTransitionStartX=");
953                        pw.print(mNextAppTransitionStartX);
954                        pw.print(" mNextAppTransitionStartY=");
955                        pw.println(mNextAppTransitionStartY);
956                pw.print("  mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
957                break;
958        }
959        if (mNextAppTransitionCallback != null) {
960            pw.print("  mNextAppTransitionCallback=");
961            pw.println(mNextAppTransitionCallback);
962        }
963    }
964
965    public void setCurrentUser(int newUserId) {
966        mCurrentUserId = newUserId;
967    }
968}
969