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