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