AppTransition.java revision 5393dff555641f5666573952498e27c90fd3edca
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, Configuration configuration) {
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            boolean useAlternateThumbnailAnimation = (configuration.smallestScreenWidthDp < 600);
704            if (useAlternateThumbnailAnimation) {
705                a = createAlternateThumbnailEnterExitAnimationLocked(
706                        getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
707                        transit, containingFrame, contentInsets);
708            } else {
709                a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
710                        appWidth, appHeight, transit);
711            }
712            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
713                String animName = mNextAppTransitionScaleUp ?
714                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
715                Slog.v(TAG, "applyAnimation:"
716                        + " anim=" + a + " nextAppTransition=" + animName
717                        + " transit=" + transit + " isEntrance=" + enter
718                        + " Callers=" + Debug.getCallers(3));
719            }
720        } else {
721            int animAttr = 0;
722            switch (transit) {
723                case TRANSIT_ACTIVITY_OPEN:
724                    animAttr = enter
725                            ? WindowAnimation_activityOpenEnterAnimation
726                            : WindowAnimation_activityOpenExitAnimation;
727                    break;
728                case TRANSIT_ACTIVITY_CLOSE:
729                    animAttr = enter
730                            ? WindowAnimation_activityCloseEnterAnimation
731                            : WindowAnimation_activityCloseExitAnimation;
732                    break;
733                case TRANSIT_TASK_OPEN:
734                    animAttr = enter
735                            ? WindowAnimation_taskOpenEnterAnimation
736                            : WindowAnimation_taskOpenExitAnimation;
737                    break;
738                case TRANSIT_TASK_CLOSE:
739                    animAttr = enter
740                            ? WindowAnimation_taskCloseEnterAnimation
741                            : WindowAnimation_taskCloseExitAnimation;
742                    break;
743                case TRANSIT_TASK_TO_FRONT:
744                    animAttr = enter
745                            ? WindowAnimation_taskToFrontEnterAnimation
746                            : WindowAnimation_taskToFrontExitAnimation;
747                    break;
748                case TRANSIT_TASK_TO_BACK:
749                    animAttr = enter
750                            ? WindowAnimation_taskToBackEnterAnimation
751                            : WindowAnimation_taskToBackExitAnimation;
752                    break;
753                case TRANSIT_WALLPAPER_OPEN:
754                    animAttr = enter
755                            ? WindowAnimation_wallpaperOpenEnterAnimation
756                            : WindowAnimation_wallpaperOpenExitAnimation;
757                    break;
758                case TRANSIT_WALLPAPER_CLOSE:
759                    animAttr = enter
760                            ? WindowAnimation_wallpaperCloseEnterAnimation
761                            : WindowAnimation_wallpaperCloseExitAnimation;
762                    break;
763                case TRANSIT_WALLPAPER_INTRA_OPEN:
764                    animAttr = enter
765                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
766                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
767                    break;
768                case TRANSIT_WALLPAPER_INTRA_CLOSE:
769                    animAttr = enter
770                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
771                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
772                    break;
773            }
774            a = animAttr != 0 ? loadAnimation(lp, animAttr) : null;
775            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
776                    "applyAnimation:"
777                    + " anim=" + a
778                    + " animAttr=0x" + Integer.toHexString(animAttr)
779                    + " transit=" + transit + " isEntrance=" + enter
780                    + " Callers=" + Debug.getCallers(3));
781        }
782        return a;
783    }
784
785    void postAnimationCallback() {
786        if (mNextAppTransitionCallback != null) {
787            mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
788            mNextAppTransitionCallback = null;
789        }
790    }
791
792    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
793                                             IRemoteCallback startedCallback) {
794        if (isTransitionSet()) {
795            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
796            mNextAppTransitionPackage = packageName;
797            mNextAppTransitionThumbnail = null;
798            mNextAppTransitionEnter = enterAnim;
799            mNextAppTransitionExit = exitAnim;
800            postAnimationCallback();
801            mNextAppTransitionCallback = startedCallback;
802        } else {
803            postAnimationCallback();
804        }
805    }
806
807    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
808                                                    int startHeight) {
809        if (isTransitionSet()) {
810            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
811            mNextAppTransitionPackage = null;
812            mNextAppTransitionThumbnail = null;
813            mNextAppTransitionStartX = startX;
814            mNextAppTransitionStartY = startY;
815            mNextAppTransitionStartWidth = startWidth;
816            mNextAppTransitionStartHeight = startHeight;
817            postAnimationCallback();
818            mNextAppTransitionCallback = null;
819        }
820    }
821
822    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
823                                           IRemoteCallback startedCallback, boolean scaleUp) {
824        if (isTransitionSet()) {
825            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
826                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
827            mNextAppTransitionPackage = null;
828            mNextAppTransitionThumbnail = srcThumb;
829            mNextAppTransitionScaleUp = scaleUp;
830            mNextAppTransitionStartX = startX;
831            mNextAppTransitionStartY = startY;
832            postAnimationCallback();
833            mNextAppTransitionCallback = startedCallback;
834        } else {
835            postAnimationCallback();
836        }
837    }
838
839    @Override
840    public String toString() {
841        return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition);
842    }
843
844    /**
845     * Returns the human readable name of a window transition.
846     *
847     * @param transition The window transition.
848     * @return The transition symbolic name.
849     */
850    public static String appTransitionToString(int transition) {
851        switch (transition) {
852            case TRANSIT_UNSET: {
853                return "TRANSIT_UNSET";
854            }
855            case TRANSIT_NONE: {
856                return "TRANSIT_NONE";
857            }
858            case TRANSIT_EXIT_MASK: {
859                return "TRANSIT_EXIT_MASK";
860            }
861            case TRANSIT_ACTIVITY_OPEN: {
862                return "TRANSIT_ACTIVITY_OPEN";
863            }
864            case TRANSIT_ACTIVITY_CLOSE: {
865                return "TRANSIT_ACTIVITY_CLOSE";
866            }
867            case TRANSIT_TASK_OPEN: {
868                return "TRANSIT_TASK_OPEN";
869            }
870            case TRANSIT_TASK_CLOSE: {
871                return "TRANSIT_TASK_CLOSE";
872            }
873            case TRANSIT_TASK_TO_FRONT: {
874                return "TRANSIT_TASK_TO_FRONT";
875            }
876            case TRANSIT_TASK_TO_BACK: {
877                return "TRANSIT_TASK_TO_BACK";
878            }
879            case TRANSIT_WALLPAPER_CLOSE: {
880                return "TRANSIT_WALLPAPER_CLOSE";
881            }
882            case TRANSIT_WALLPAPER_OPEN: {
883                return "TRANSIT_WALLPAPER_OPEN";
884            }
885            case TRANSIT_WALLPAPER_INTRA_OPEN: {
886                return "TRANSIT_WALLPAPER_INTRA_OPEN";
887            }
888            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
889                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
890            }
891            default: {
892                return "<UNKNOWN>";
893            }
894        }
895    }
896
897    private String appStateToString() {
898        switch (mAppTransitionState) {
899            case APP_STATE_IDLE:
900                return "APP_STATE_IDLE";
901            case APP_STATE_READY:
902                return "APP_STATE_READY";
903            case APP_STATE_RUNNING:
904                return "APP_STATE_RUNNING";
905            case APP_STATE_TIMEOUT:
906                return "APP_STATE_TIMEOUT";
907            default:
908                return "unknown state=" + mAppTransitionState;
909        }
910    }
911
912    private String transitTypeToString() {
913        switch (mNextAppTransitionType) {
914            case NEXT_TRANSIT_TYPE_NONE:
915                return "NEXT_TRANSIT_TYPE_NONE";
916            case NEXT_TRANSIT_TYPE_CUSTOM:
917                return "NEXT_TRANSIT_TYPE_CUSTOM";
918            case NEXT_TRANSIT_TYPE_SCALE_UP:
919                return "NEXT_TRANSIT_TYPE_SCALE_UP";
920            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
921                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
922            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
923                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
924            default:
925                return "unknown type=" + mNextAppTransitionType;
926        }
927    }
928
929    @Override
930    public void dump(PrintWriter pw) {
931        pw.print(" " + this);
932        pw.print("  mAppTransitionState="); pw.println(appStateToString());
933        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
934            pw.print("  mNextAppTransitionType="); pw.println(transitTypeToString());
935        }
936        switch (mNextAppTransitionType) {
937            case NEXT_TRANSIT_TYPE_CUSTOM:
938                pw.print("  mNextAppTransitionPackage=");
939                        pw.println(mNextAppTransitionPackage);
940                pw.print("  mNextAppTransitionEnter=0x");
941                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
942                        pw.print(" mNextAppTransitionExit=0x");
943                        pw.println(Integer.toHexString(mNextAppTransitionExit));
944                break;
945            case NEXT_TRANSIT_TYPE_SCALE_UP:
946                pw.print("  mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
947                        pw.print(" mNextAppTransitionStartY=");
948                        pw.println(mNextAppTransitionStartY);
949                pw.print("  mNextAppTransitionStartWidth=");
950                        pw.print(mNextAppTransitionStartWidth);
951                        pw.print(" mNextAppTransitionStartHeight=");
952                        pw.println(mNextAppTransitionStartHeight);
953                break;
954            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
955            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
956                pw.print("  mNextAppTransitionThumbnail=");
957                        pw.print(mNextAppTransitionThumbnail);
958                        pw.print(" mNextAppTransitionStartX=");
959                        pw.print(mNextAppTransitionStartX);
960                        pw.print(" mNextAppTransitionStartY=");
961                        pw.println(mNextAppTransitionStartY);
962                pw.print("  mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp);
963                break;
964        }
965        if (mNextAppTransitionCallback != null) {
966            pw.print("  mNextAppTransitionCallback=");
967            pw.println(mNextAppTransitionCallback);
968        }
969    }
970
971    public void setCurrentUser(int newUserId) {
972        mCurrentUserId = newUserId;
973    }
974}
975