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