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