AppTransition.java revision d143142f3d26564feda324968b0469c0f600f0ad
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.annotation.Nullable;
20import android.content.Context;
21import android.content.res.Configuration;
22import android.graphics.Bitmap;
23import android.graphics.Rect;
24import android.os.Debug;
25import android.os.Handler;
26import android.os.IBinder;
27import android.os.IRemoteCallback;
28import android.util.Slog;
29import android.util.SparseArray;
30import android.view.AppTransitionAnimationSpec;
31import android.view.WindowManager;
32import android.view.animation.AlphaAnimation;
33import android.view.animation.Animation;
34import android.view.animation.AnimationSet;
35import android.view.animation.AnimationUtils;
36import android.view.animation.ClipRectAnimation;
37import android.view.animation.ClipRectLRAnimation;
38import android.view.animation.ClipRectTBAnimation;
39import android.view.animation.Interpolator;
40import android.view.animation.PathInterpolator;
41import android.view.animation.ScaleAnimation;
42import android.view.animation.TranslateAnimation;
43import android.view.animation.TranslateYAnimation;
44
45import com.android.internal.util.DumpUtils.Dump;
46import com.android.server.AttributeCache;
47import com.android.server.wm.WindowManagerService.H;
48
49import java.io.PrintWriter;
50import java.util.ArrayList;
51
52import static android.view.WindowManagerInternal.AppTransitionListener;
53import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
54import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
55import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
56import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
57import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
58import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
59import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
60import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
61import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
62import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
63import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation;
64import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
65import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
66import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
67import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation;
68import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
69import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation;
70import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
71import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation;
72import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
73import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
74import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
75
76// State management of app transitions.  When we are preparing for a
77// transition, mNextAppTransition will be the kind of transition to
78// perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
79// mOpeningApps and mClosingApps are the lists of tokens that will be
80// made visible or hidden at the next transition.
81public class AppTransition implements Dump {
82    private static final String TAG = "AppTransition";
83    private static final boolean DEBUG_APP_TRANSITIONS =
84            WindowManagerService.DEBUG_APP_TRANSITIONS;
85    private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM;
86    private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
87
88    /** Not set up for a transition. */
89    public static final int TRANSIT_UNSET = -1;
90    /** No animation for transition. */
91    public static final int TRANSIT_NONE = 0;
92    /** A window in a new activity is being opened on top of an existing one in the same task. */
93    public static final int TRANSIT_ACTIVITY_OPEN = 6;
94    /** The window in the top-most activity is being closed to reveal the
95     * previous activity in the same task. */
96    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
97    /** A window in a new task is being opened on top of an existing one
98     * in another activity's task. */
99    public static final int TRANSIT_TASK_OPEN = 8;
100    /** A window in the top-most activity is being closed to reveal the
101     * previous activity in a different task. */
102    public static final int TRANSIT_TASK_CLOSE = 9;
103    /** A window in an existing task is being displayed on top of an existing one
104     * in another activity's task. */
105    public static final int TRANSIT_TASK_TO_FRONT = 10;
106    /** A window in an existing task is being put below all other tasks. */
107    public static final int TRANSIT_TASK_TO_BACK = 11;
108    /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
109     * does, effectively closing the wallpaper. */
110    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
111    /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
112     * effectively opening the wallpaper. */
113    public static final int TRANSIT_WALLPAPER_OPEN = 13;
114    /** A window in a new activity is being opened on top of an existing one, and both are on top
115     * of the wallpaper. */
116    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
117    /** The window in the top-most activity is being closed to reveal the previous activity, and
118     * both are on top of the wallpaper. */
119    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
120    /** A window in a new task is being opened behind an existing one in another activity's task.
121     * The new window will show briefly and then be gone. */
122    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
123    /** A window in a task is being animated in-place. */
124    public static final int TRANSIT_TASK_IN_PLACE = 17;
125    /** An activity is being relaunched (e.g. due to configuration change). */
126    public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
127
128    /** Fraction of animation at which the recents thumbnail stays completely transparent */
129    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
130    /** Fraction of animation at which the recents thumbnail becomes completely transparent */
131    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
132
133    private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
134    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
135    private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
136
137    private final Context mContext;
138    private final Handler mH;
139
140    private int mNextAppTransition = TRANSIT_UNSET;
141
142    private static final int NEXT_TRANSIT_TYPE_NONE = 0;
143    private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
144    private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
145    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
146    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
147    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
148    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
149    private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7;
150    private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8;
151    private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
152
153    // These are the possible states for the enter/exit activities during a thumbnail transition
154    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
155    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
156    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
157    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
158
159    private String mNextAppTransitionPackage;
160    // Used for thumbnail transitions. True if we're scaling up, false if scaling down
161    private boolean mNextAppTransitionScaleUp;
162    private IRemoteCallback mNextAppTransitionCallback;
163    private int mNextAppTransitionEnter;
164    private int mNextAppTransitionExit;
165    private int mNextAppTransitionInPlace;
166
167    // Keyed by task id.
168    private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
169            = new SparseArray<>();
170    private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
171
172    private Rect mNextAppTransitionInsets = new Rect();
173
174    private Rect mTmpFromClipRect = new Rect();
175    private Rect mTmpToClipRect = new Rect();
176
177    private final Rect mTmpStartRect = new Rect();
178
179    private final static int APP_STATE_IDLE = 0;
180    private final static int APP_STATE_READY = 1;
181    private final static int APP_STATE_RUNNING = 2;
182    private final static int APP_STATE_TIMEOUT = 3;
183    private int mAppTransitionState = APP_STATE_IDLE;
184
185    private final int mConfigShortAnimTime;
186    private final Interpolator mDecelerateInterpolator;
187    private final Interpolator mThumbnailFadeInInterpolator;
188    private final Interpolator mThumbnailFadeOutInterpolator;
189    private final Interpolator mLinearOutSlowInInterpolator;
190    private final Interpolator mFastOutLinearInInterpolator;
191    private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
192
193    /** Interpolator to be used for animations that respond directly to a touch */
194    private final Interpolator mTouchResponseInterpolator =
195            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
196
197    private final int mClipRevealTranslationY;
198
199    private int mCurrentUserId = 0;
200
201    private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
202
203    AppTransition(Context context, Handler h) {
204        mContext = context;
205        mH = h;
206        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
207                com.android.internal.R.interpolator.linear_out_slow_in);
208        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
209                com.android.internal.R.interpolator.fast_out_linear_in);
210        mConfigShortAnimTime = context.getResources().getInteger(
211                com.android.internal.R.integer.config_shortAnimTime);
212        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
213                com.android.internal.R.interpolator.decelerate_cubic);
214        mThumbnailFadeInInterpolator = new Interpolator() {
215            @Override
216            public float getInterpolation(float input) {
217                // Linear response for first fraction, then complete after that.
218                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
219                    return 0f;
220                }
221                float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
222                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
223                return mFastOutLinearInInterpolator.getInterpolation(t);
224            }
225        };
226        mThumbnailFadeOutInterpolator = new Interpolator() {
227            @Override
228            public float getInterpolation(float input) {
229                // Linear response for first fraction, then complete after that.
230                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
231                    float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
232                    return mLinearOutSlowInInterpolator.getInterpolation(t);
233                }
234                return 1f;
235            }
236        };
237        mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
238                * mContext.getResources().getDisplayMetrics().density);
239    }
240
241    boolean isTransitionSet() {
242        return mNextAppTransition != TRANSIT_UNSET;
243    }
244
245    boolean isTransitionNone() {
246        return mNextAppTransition == TRANSIT_NONE;
247    }
248
249    boolean isTransitionEqual(int transit) {
250        return mNextAppTransition == transit;
251    }
252
253    int getAppTransition() {
254        return mNextAppTransition;
255     }
256
257    void setAppTransition(int transit) {
258        mNextAppTransition = transit;
259    }
260
261    boolean isReady() {
262        return mAppTransitionState == APP_STATE_READY
263                || mAppTransitionState == APP_STATE_TIMEOUT;
264    }
265
266    void setReady() {
267        mAppTransitionState = APP_STATE_READY;
268    }
269
270    boolean isRunning() {
271        return mAppTransitionState == APP_STATE_RUNNING;
272    }
273
274    void setIdle() {
275        mAppTransitionState = APP_STATE_IDLE;
276    }
277
278    boolean isTimeout() {
279        return mAppTransitionState == APP_STATE_TIMEOUT;
280    }
281
282    void setTimeout() {
283        mAppTransitionState = APP_STATE_TIMEOUT;
284    }
285
286    Bitmap getAppTransitionThumbnailHeader(int taskId) {
287        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
288        return spec != null ? spec.bitmap : null;
289    }
290
291    /** Returns whether the next thumbnail transition is aspect scaled up. */
292    boolean isNextThumbnailTransitionAspectScaled() {
293        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
294                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
295    }
296
297    /** Returns whether the next thumbnail transition is scaling up. */
298    boolean isNextThumbnailTransitionScaleUp() {
299        return mNextAppTransitionScaleUp;
300    }
301
302    boolean prepare() {
303        if (!isRunning()) {
304            mAppTransitionState = APP_STATE_IDLE;
305            notifyAppTransitionPendingLocked();
306            return true;
307        }
308        return false;
309    }
310
311    void goodToGo(AppWindowAnimator openingAppAnimator, AppWindowAnimator closingAppAnimator) {
312        mNextAppTransition = TRANSIT_UNSET;
313        mAppTransitionState = APP_STATE_RUNNING;
314        notifyAppTransitionStartingLocked(
315                openingAppAnimator != null ? openingAppAnimator.mAppToken.token : null,
316                closingAppAnimator != null ? closingAppAnimator.mAppToken.token : null,
317                openingAppAnimator != null ? openingAppAnimator.animation : null,
318                closingAppAnimator != null ? closingAppAnimator.animation : null);
319    }
320
321    void clear() {
322        mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
323        mNextAppTransitionPackage = null;
324        mNextAppTransitionAnimationsSpecs.clear();
325    }
326
327    void freeze() {
328        setAppTransition(AppTransition.TRANSIT_UNSET);
329        clear();
330        setReady();
331        notifyAppTransitionCancelledLocked();
332    }
333
334    void registerListenerLocked(AppTransitionListener listener) {
335        mListeners.add(listener);
336    }
337
338    public void notifyAppTransitionFinishedLocked(IBinder token) {
339        for (int i = 0; i < mListeners.size(); i++) {
340            mListeners.get(i).onAppTransitionFinishedLocked(token);
341        }
342    }
343
344    private void notifyAppTransitionPendingLocked() {
345        for (int i = 0; i < mListeners.size(); i++) {
346            mListeners.get(i).onAppTransitionPendingLocked();
347        }
348    }
349
350    private void notifyAppTransitionCancelledLocked() {
351        for (int i = 0; i < mListeners.size(); i++) {
352            mListeners.get(i).onAppTransitionCancelledLocked();
353        }
354    }
355
356    private void notifyAppTransitionStartingLocked(IBinder openToken,
357            IBinder closeToken, Animation openAnimation, Animation closeAnimation) {
358        for (int i = 0; i < mListeners.size(); i++) {
359            mListeners.get(i).onAppTransitionStartingLocked(openToken, closeToken, openAnimation,
360                    closeAnimation);
361        }
362    }
363
364    private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
365        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
366                + (lp != null ? lp.packageName : null)
367                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
368        if (lp != null && lp.windowAnimations != 0) {
369            // If this is a system resource, don't try to load it from the
370            // application resources.  It is nice to avoid loading application
371            // resources if we can.
372            String packageName = lp.packageName != null ? lp.packageName : "android";
373            int resId = lp.windowAnimations;
374            if ((resId&0xFF000000) == 0x01000000) {
375                packageName = "android";
376            }
377            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
378                    + packageName);
379            return AttributeCache.instance().get(packageName, resId,
380                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
381        }
382        return null;
383    }
384
385    private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
386        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
387                + packageName + " resId=0x" + Integer.toHexString(resId));
388        if (packageName != null) {
389            if ((resId&0xFF000000) == 0x01000000) {
390                packageName = "android";
391            }
392            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
393                    + packageName);
394            return AttributeCache.instance().get(packageName, resId,
395                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
396        }
397        return null;
398    }
399
400    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
401        int anim = 0;
402        Context context = mContext;
403        if (animAttr >= 0) {
404            AttributeCache.Entry ent = getCachedAnimations(lp);
405            if (ent != null) {
406                context = ent.context;
407                anim = ent.array.getResourceId(animAttr, 0);
408            }
409        }
410        if (anim != 0) {
411            return AnimationUtils.loadAnimation(context, anim);
412        }
413        return null;
414    }
415
416    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
417        Context context = mContext;
418        if (resId >= 0) {
419            AttributeCache.Entry ent = getCachedAnimations(lp);
420            if (ent != null) {
421                context = ent.context;
422            }
423            return AnimationUtils.loadAnimation(context, resId);
424        }
425        return null;
426    }
427
428    private Animation loadAnimationRes(String packageName, int resId) {
429        int anim = 0;
430        Context context = mContext;
431        if (resId >= 0) {
432            AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
433            if (ent != null) {
434                context = ent.context;
435                anim = resId;
436            }
437        }
438        if (anim != 0) {
439            return AnimationUtils.loadAnimation(context, anim);
440        }
441        return null;
442    }
443
444    /**
445     * Compute the pivot point for an animation that is scaling from a small
446     * rect on screen to a larger rect.  The pivot point varies depending on
447     * the distance between the inner and outer edges on both sides.  This
448     * function computes the pivot point for one dimension.
449     * @param startPos  Offset from left/top edge of outer rectangle to
450     * left/top edge of inner rectangle.
451     * @param finalScale The scaling factor between the size of the outer
452     * and inner rectangles.
453     */
454    private static float computePivot(int startPos, float finalScale) {
455        final float denom = finalScale-1;
456        if (Math.abs(denom) < .0001f) {
457            return startPos;
458        }
459        return -startPos / denom;
460    }
461
462    private Animation createScaleUpAnimationLocked(
463            int transit, boolean enter, int appWidth, int appHeight) {
464        Animation a = null;
465        getDefaultNextAppTransitionStartRect(mTmpStartRect);
466        if (enter) {
467            // Entering app zooms out from the center of the initial rect.
468            float scaleW = mTmpStartRect.width() / (float) appWidth;
469            float scaleH = mTmpStartRect.height() / (float) appHeight;
470            Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
471                    computePivot(mTmpStartRect.left, scaleW),
472                    computePivot(mTmpStartRect.right, scaleH));
473            scale.setInterpolator(mDecelerateInterpolator);
474
475            Animation alpha = new AlphaAnimation(0, 1);
476            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
477
478            AnimationSet set = new AnimationSet(false);
479            set.addAnimation(scale);
480            set.addAnimation(alpha);
481            set.setDetachWallpaper(true);
482            a = set;
483        } else  if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
484                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
485            // If we are on top of the wallpaper, we need an animation that
486            // correctly handles the wallpaper staying static behind all of
487            // the animated elements.  To do this, will just have the existing
488            // element fade out.
489            a = new AlphaAnimation(1, 0);
490            a.setDetachWallpaper(true);
491        } else {
492            // For normal animations, the exiting element just holds in place.
493            a = new AlphaAnimation(1, 1);
494        }
495
496        // Pick the desired duration.  If this is an inter-activity transition,
497        // it  is the standard duration for that.  Otherwise we use the longer
498        // task transition duration.
499        final long duration;
500        switch (transit) {
501            case TRANSIT_ACTIVITY_OPEN:
502            case TRANSIT_ACTIVITY_CLOSE:
503                duration = mConfigShortAnimTime;
504                break;
505            default:
506                duration = DEFAULT_APP_TRANSITION_DURATION;
507                break;
508        }
509        a.setDuration(duration);
510        a.setFillAfter(true);
511        a.setInterpolator(mDecelerateInterpolator);
512        a.initialize(appWidth, appHeight, appWidth, appHeight);
513        return a;
514    }
515
516    private void getDefaultNextAppTransitionStartRect(Rect rect) {
517        if (mDefaultNextAppTransitionAnimationSpec == null ||
518                mDefaultNextAppTransitionAnimationSpec.rect == null) {
519            Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
520            rect.setEmpty();
521        } else {
522            rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
523        }
524    }
525
526    void getNextAppTransitionStartRect(int taskId, Rect rect) {
527        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
528        if (spec == null || spec.rect == null) {
529            Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
530                    new Throwable());
531            rect.setEmpty();
532        } else {
533            rect.set(spec.rect);
534        }
535    }
536
537    private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height) {
538        mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
539                null /* bitmap */, new Rect(left, top, left + width, top + height));
540    }
541
542    private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame) {
543        final Animation anim;
544        if (enter) {
545            // Reveal will expand and move faster in horizontal direction
546
547            final int appWidth = appFrame.width();
548            final int appHeight = appFrame.height();
549            getDefaultNextAppTransitionStartRect(mTmpStartRect);
550
551            float t = 0f;
552            if (appHeight > 0) {
553                t = (float) mTmpStartRect.left / appHeight;
554            }
555            int translationY = mClipRevealTranslationY
556                    + (int)(appHeight / 7f * t);
557
558            int centerX = mTmpStartRect.centerX();
559            int centerY = mTmpStartRect.centerY();
560            int halfWidth = mTmpStartRect.width() / 2;
561            int halfHeight = mTmpStartRect.height() / 2;
562
563            // Clip third of the from size of launch icon, expand to full width/height
564            Animation clipAnimLR = new ClipRectLRAnimation(
565                    centerX - halfWidth, centerX + halfWidth, 0, appWidth);
566            clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
567            clipAnimLR.setDuration((long) (DEFAULT_APP_TRANSITION_DURATION / 2.5f));
568            Animation clipAnimTB = new ClipRectTBAnimation(centerY - halfHeight - translationY,
569                    centerY + halfHeight/ 2 - translationY, 0, appHeight);
570            clipAnimTB.setInterpolator(mTouchResponseInterpolator);
571            clipAnimTB.setDuration(DEFAULT_APP_TRANSITION_DURATION);
572
573            TranslateYAnimation translateY = new TranslateYAnimation(
574                    Animation.ABSOLUTE, translationY, Animation.ABSOLUTE, 0);
575            translateY.setInterpolator(mLinearOutSlowInInterpolator);
576            translateY.setDuration(DEFAULT_APP_TRANSITION_DURATION);
577
578            // Quick fade-in from icon to app window
579            final int alphaDuration = DEFAULT_APP_TRANSITION_DURATION / 4;
580            AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
581            alpha.setDuration(alphaDuration);
582            alpha.setInterpolator(mLinearOutSlowInInterpolator);
583
584            AnimationSet set = new AnimationSet(false);
585            set.addAnimation(clipAnimLR);
586            set.addAnimation(clipAnimTB);
587            set.addAnimation(translateY);
588            set.addAnimation(alpha);
589            set.setZAdjustment(Animation.ZORDER_TOP);
590            set.initialize(appWidth, appHeight, appWidth, appHeight);
591            anim = set;
592        } else {
593            final long duration;
594            switch (transit) {
595                case TRANSIT_ACTIVITY_OPEN:
596                case TRANSIT_ACTIVITY_CLOSE:
597                    duration = mConfigShortAnimTime;
598                    break;
599                default:
600                    duration = DEFAULT_APP_TRANSITION_DURATION;
601                    break;
602            }
603            if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
604                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
605                // If we are on top of the wallpaper, we need an animation that
606                // correctly handles the wallpaper staying static behind all of
607                // the animated elements.  To do this, will just have the existing
608                // element fade out.
609                anim = new AlphaAnimation(1, 0);
610                anim.setDetachWallpaper(true);
611            } else {
612                // For normal animations, the exiting element just holds in place.
613                anim = new AlphaAnimation(1, 1);
614            }
615            anim.setInterpolator(mDecelerateInterpolator);
616            anim.setDuration(duration);
617            anim.setFillAfter(true);
618        }
619        return anim;
620    }
621
622    /**
623     * Prepares the specified animation with a standard duration, interpolator, etc.
624     */
625    Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
626            int duration, Interpolator interpolator) {
627        if (duration > 0) {
628            a.setDuration(duration);
629        }
630        a.setFillAfter(true);
631        a.setInterpolator(interpolator);
632        a.initialize(appWidth, appHeight, appWidth, appHeight);
633        return a;
634    }
635
636    /**
637     * Prepares the specified animation with a standard duration, interpolator, etc.
638     */
639    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
640        // Pick the desired duration.  If this is an inter-activity transition,
641        // it  is the standard duration for that.  Otherwise we use the longer
642        // task transition duration.
643        final int duration;
644        switch (transit) {
645            case TRANSIT_ACTIVITY_OPEN:
646            case TRANSIT_ACTIVITY_CLOSE:
647                duration = mConfigShortAnimTime;
648                break;
649            default:
650                duration = DEFAULT_APP_TRANSITION_DURATION;
651                break;
652        }
653        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
654                mDecelerateInterpolator);
655    }
656
657    /**
658     * Return the current thumbnail transition state.
659     */
660    int getThumbnailTransitionState(boolean enter) {
661        if (enter) {
662            if (mNextAppTransitionScaleUp) {
663                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
664            } else {
665                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
666            }
667        } else {
668            if (mNextAppTransitionScaleUp) {
669                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
670            } else {
671                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
672            }
673        }
674    }
675
676    /**
677     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
678     * when a thumbnail is specified with the pending animation override.
679     */
680    Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, Bitmap thumbnailHeader,
681            final int taskId) {
682        Animation a;
683        final int thumbWidthI = thumbnailHeader.getWidth();
684        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
685        final int thumbHeightI = thumbnailHeader.getHeight();
686        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
687        final int appWidth = appRect.width();
688
689        float scaleW = appWidth / thumbWidth;
690        float unscaledHeight = thumbHeight * scaleW;
691        getNextAppTransitionStartRect(taskId, mTmpStartRect);
692        float unscaledStartY = mTmpStartRect.top - (unscaledHeight - thumbHeight) / 2f;
693        if (mNextAppTransitionScaleUp) {
694            // Animation up from the thumbnail to the full screen
695            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
696                    mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
697            scale.setInterpolator(mTouchResponseInterpolator);
698            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
699            Animation alpha = new AlphaAnimation(1, 0);
700            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
701            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
702            final float toX = appRect.left + appRect.width() / 2 -
703                    (mTmpStartRect.left + thumbWidth / 2);
704            final float toY = appRect.top + mNextAppTransitionInsets.top + -unscaledStartY;
705            Animation translate = new TranslateAnimation(0, toX, 0, toY);
706            translate.setInterpolator(mTouchResponseInterpolator);
707            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
708
709            // This AnimationSet uses the Interpolators assigned above.
710            AnimationSet set = new AnimationSet(false);
711            set.addAnimation(scale);
712            set.addAnimation(alpha);
713            set.addAnimation(translate);
714            a = set;
715        } else {
716            // Animation down from the full screen to the thumbnail
717            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
718                    mTmpStartRect.left + (thumbWidth / 2f), mTmpStartRect.top + (thumbHeight / 2f));
719            scale.setInterpolator(mTouchResponseInterpolator);
720            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
721            Animation alpha = new AlphaAnimation(0f, 1f);
722            alpha.setInterpolator(mThumbnailFadeInInterpolator);
723            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
724            Animation translate = new TranslateAnimation(0, 0, -unscaledStartY +
725                    mNextAppTransitionInsets.top, 0);
726            translate.setInterpolator(mTouchResponseInterpolator);
727            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
728
729            // This AnimationSet uses the Interpolators assigned above.
730            AnimationSet set = new AnimationSet(false);
731            set.addAnimation(scale);
732            set.addAnimation(alpha);
733            set.addAnimation(translate);
734            a = set;
735
736        }
737        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
738                mTouchResponseInterpolator);
739    }
740
741    /**
742     * This alternate animation is created when we are doing a thumbnail transition, for the
743     * activity that is leaving, and the activity that is entering.
744     */
745    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
746            int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
747            Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow,
748            int taskId) {
749        Animation a;
750        getDefaultNextAppTransitionStartRect(mTmpStartRect);
751        final int thumbWidthI = mTmpStartRect.width();
752        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
753        final int thumbHeightI = mTmpStartRect.height();
754        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
755
756        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
757        float scale = 1f;
758        int scaledTopDecor = 0;
759
760        switch (thumbTransitState) {
761            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
762                if (resizedWindow) {
763                    a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
764                            containingFrame, surfaceInsets, taskId);
765                } else {
766                    // App window scaling up to become full screen
767                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
768                        // In portrait, we scale the width and clip to the top/left square
769                        scale = thumbWidth / appWidth;
770                        scaledTopDecor = (int) (scale * contentInsets.top);
771                        int unscaledThumbHeight = (int) (thumbHeight / scale);
772                        mTmpFromClipRect.set(containingFrame);
773                        mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight);
774                        mTmpToClipRect.set(containingFrame);
775                    } else {
776                        // In landscape, we scale the height and clip to the top/left square
777                        scale = thumbHeight / (appHeight - contentInsets.top);
778                        scaledTopDecor = (int) (scale * contentInsets.top);
779                        int unscaledThumbWidth = (int) (thumbWidth / scale);
780                        mTmpFromClipRect.set(containingFrame);
781                        mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth);
782                        mTmpToClipRect.set(containingFrame);
783                    }
784                    // exclude top screen decor (status bar) region from the source clip.
785                    mTmpFromClipRect.top = contentInsets.top;
786
787                    mNextAppTransitionInsets.set(contentInsets);
788
789                    Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
790                            computePivot(mTmpStartRect.left, scale),
791                            computePivot(mTmpStartRect.top, scale));
792                    Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
793                    Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
794
795                    AnimationSet set = new AnimationSet(true);
796                    set.addAnimation(clipAnim);
797                    set.addAnimation(scaleAnim);
798                    set.addAnimation(translateAnim);
799                    a = set;
800                }
801                break;
802            }
803            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
804                // Previous app window during the scale up
805                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
806                    // Fade out the source activity if we are animating to a wallpaper
807                    // activity.
808                    a = new AlphaAnimation(1, 0);
809                } else {
810                    a = new AlphaAnimation(1, 1);
811                }
812                break;
813            }
814            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
815                // Target app window during the scale down
816                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
817                    // Fade in the destination activity if we are animating from a wallpaper
818                    // activity.
819                    a = new AlphaAnimation(0, 1);
820                } else {
821                    a = new AlphaAnimation(1, 1);
822                }
823                break;
824            }
825            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
826                // App window scaling down from full screen
827                if (orientation == Configuration.ORIENTATION_PORTRAIT) {
828                    // In portrait, we scale the width and clip to the top/left square
829                    scale = thumbWidth / appWidth;
830                    scaledTopDecor = (int) (scale * contentInsets.top);
831                    int unscaledThumbHeight = (int) (thumbHeight / scale);
832                    mTmpFromClipRect.set(containingFrame);
833                    mTmpToClipRect.set(containingFrame);
834                    mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight);
835                } else {
836                    // In landscape, we scale the height and clip to the top/left square
837                    scale = thumbHeight / (appHeight - contentInsets.top);
838                    scaledTopDecor = (int) (scale * contentInsets.top);
839                    int unscaledThumbWidth = (int) (thumbWidth / scale);
840                    mTmpFromClipRect.set(containingFrame);
841                    mTmpToClipRect.set(containingFrame);
842                    mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth);
843                }
844                // exclude top screen decor (status bar) region from the destination clip.
845                mTmpToClipRect.top = contentInsets.top;
846
847                mNextAppTransitionInsets.set(contentInsets);
848
849                Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
850                        computePivot(mTmpStartRect.left, scale),
851                        computePivot(mTmpStartRect.top, scale));
852                Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
853                Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
854
855                AnimationSet set = new AnimationSet(true);
856                set.addAnimation(clipAnim);
857                set.addAnimation(scaleAnim);
858                set.addAnimation(translateAnim);
859
860                a = set;
861                a.setZAdjustment(Animation.ZORDER_TOP);
862                break;
863            }
864            default:
865                throw new RuntimeException("Invalid thumbnail transition state");
866        }
867
868        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
869                THUMBNAIL_APP_TRANSITION_DURATION);
870        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
871                mTouchResponseInterpolator);
872    }
873
874    private Animation createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
875            Rect frame, @Nullable Rect surfaceInsets, int taskId) {
876        getNextAppTransitionStartRect(taskId, mTmpStartRect);
877        float width = frame.width();
878        float height = frame.height();
879        float scaleWidth = mTmpStartRect.width() / width;
880        float scaleHeight = mTmpStartRect.height() / height;
881        AnimationSet set = new AnimationSet(true);
882        int surfaceInsetsHorizontal = surfaceInsets == null
883                ? 0 : surfaceInsets.left + surfaceInsets.right;
884        int surfaceInsetsVertical = surfaceInsets == null
885                ? 0 : surfaceInsets.top + surfaceInsets.bottom;
886        // We want the scaling to happen from the center of the surface. In order to achieve that,
887        // we need to account for surface insets that will be used to enlarge the surface.
888        ScaleAnimation scale = new ScaleAnimation(scaleWidth, 1, scaleHeight, 1,
889                (width + surfaceInsetsHorizontal) / 2, (height + surfaceInsetsVertical) / 2);
890        int fromX = mTmpStartRect.left + mTmpStartRect.width() / 2
891                - (frame.left + frame.width() / 2);
892        int fromY = mTmpStartRect.top + mTmpStartRect.height() / 2
893                - (frame.top + frame.height() / 2);
894        TranslateAnimation translation = new TranslateAnimation(fromX, 0, fromY, 0);
895        set.addAnimation(scale);
896        set.addAnimation(translation);
897        return set;
898    }
899
900    /**
901     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
902     * when a thumbnail is specified with the pending animation override.
903     */
904    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
905            Bitmap thumbnailHeader) {
906        Animation a;
907        getDefaultNextAppTransitionStartRect(mTmpStartRect);
908        final int thumbWidthI = thumbnailHeader.getWidth();
909        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
910        final int thumbHeightI = thumbnailHeader.getHeight();
911        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
912
913        if (mNextAppTransitionScaleUp) {
914            // Animation for the thumbnail zooming from its initial size to the full screen
915            float scaleW = appWidth / thumbWidth;
916            float scaleH = appHeight / thumbHeight;
917            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
918                    computePivot(mTmpStartRect.left, 1 / scaleW),
919                    computePivot(mTmpStartRect.top, 1 / scaleH));
920            scale.setInterpolator(mDecelerateInterpolator);
921
922            Animation alpha = new AlphaAnimation(1, 0);
923            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
924
925            // This AnimationSet uses the Interpolators assigned above.
926            AnimationSet set = new AnimationSet(false);
927            set.addAnimation(scale);
928            set.addAnimation(alpha);
929            a = set;
930        } else {
931            // Animation for the thumbnail zooming down from the full screen to its final size
932            float scaleW = appWidth / thumbWidth;
933            float scaleH = appHeight / thumbHeight;
934            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
935                    computePivot(mTmpStartRect.left, 1 / scaleW),
936                    computePivot(mTmpStartRect.top, 1 / scaleH));
937        }
938
939        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
940    }
941
942    /**
943     * This animation is created when we are doing a thumbnail transition, for the activity that is
944     * leaving, and the activity that is entering.
945     */
946    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
947            int appHeight, int transit, int taskId) {
948        Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
949        Animation a;
950        getDefaultNextAppTransitionStartRect(mTmpStartRect);
951        final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
952        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
953        final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
954        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
955
956        switch (thumbTransitState) {
957            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
958                // Entering app scales up with the thumbnail
959                float scaleW = thumbWidth / appWidth;
960                float scaleH = thumbHeight / appHeight;
961                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
962                        computePivot(mTmpStartRect.left, scaleW),
963                        computePivot(mTmpStartRect.top, scaleH));
964                break;
965            }
966            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
967                // Exiting app while the thumbnail is scaling up should fade or stay in place
968                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
969                    // Fade out while bringing up selected activity. This keeps the
970                    // current activity from showing through a launching wallpaper
971                    // activity.
972                    a = new AlphaAnimation(1, 0);
973                } else {
974                    // noop animation
975                    a = new AlphaAnimation(1, 1);
976                }
977                break;
978            }
979            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
980                // Entering the other app, it should just be visible while we scale the thumbnail
981                // down above it
982                a = new AlphaAnimation(1, 1);
983                break;
984            }
985            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
986                // Exiting the current app, the app should scale down with the thumbnail
987                float scaleW = thumbWidth / appWidth;
988                float scaleH = thumbHeight / appHeight;
989                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
990                        computePivot(mTmpStartRect.left, scaleW),
991                        computePivot(mTmpStartRect.top, scaleH));
992
993                Animation alpha = new AlphaAnimation(1, 0);
994
995                AnimationSet set = new AnimationSet(true);
996                set.addAnimation(scale);
997                set.addAnimation(alpha);
998                set.setZAdjustment(Animation.ZORDER_TOP);
999                a = set;
1000                break;
1001            }
1002            default:
1003                throw new RuntimeException("Invalid thumbnail transition state");
1004        }
1005
1006        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1007    }
1008
1009    private Animation createRelaunchAnimation(int appWidth, int appHeight) {
1010        getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
1011        final int left = mTmpFromClipRect.left;
1012        final int top = mTmpFromClipRect.top;
1013        mTmpFromClipRect.offset(-left, -top);
1014        mTmpToClipRect.set(0, 0, appWidth, appHeight);
1015        AnimationSet set = new AnimationSet(true);
1016        ClipRectAnimation clip = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
1017        TranslateAnimation translate = new TranslateAnimation(left, 0, top, 0);
1018        clip.setInterpolator(mDecelerateInterpolator);
1019        set.addAnimation(clip);
1020        set.addAnimation(translate);
1021        set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
1022        return set;
1023    }
1024
1025    /**
1026     * @return true if and only if the first frame of the transition can be skipped, i.e. the first
1027     *         frame of the transition doesn't change the visuals on screen, so we can start
1028     *         directly with the second one
1029     */
1030    boolean canSkipFirstFrame() {
1031        return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
1032                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
1033                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1034    }
1035
1036    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
1037            int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
1038            @Nullable Rect surfaceInsets, Rect appFrame, boolean isVoiceInteraction,
1039            boolean resizedWindow, int taskId) {
1040        Animation a;
1041        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
1042                || transit == TRANSIT_TASK_OPEN
1043                || transit == TRANSIT_TASK_TO_FRONT)) {
1044            a = loadAnimationRes(lp, enter
1045                    ? com.android.internal.R.anim.voice_activity_open_enter
1046                    : com.android.internal.R.anim.voice_activity_open_exit);
1047            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1048                    "applyAnimation voice:"
1049                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1050                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1051        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
1052                || transit == TRANSIT_TASK_CLOSE
1053                || transit == TRANSIT_TASK_TO_BACK)) {
1054            a = loadAnimationRes(lp, enter
1055                    ? com.android.internal.R.anim.voice_activity_close_enter
1056                    : com.android.internal.R.anim.voice_activity_close_exit);
1057            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1058                    "applyAnimation voice:"
1059                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1060                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1061        } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
1062            a = createRelaunchAnimation(appWidth, appHeight);
1063        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
1064            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
1065                    mNextAppTransitionEnter : mNextAppTransitionExit);
1066            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1067                    "applyAnimation:"
1068                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
1069                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1070                    + " Callers=" + Debug.getCallers(3));
1071        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
1072            a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
1073            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1074                    "applyAnimation:"
1075                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
1076                    + " transit=" + appTransitionToString(transit)
1077                    + " Callers=" + Debug.getCallers(3));
1078        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
1079            a = createClipRevealAnimationLocked(transit, enter, appFrame);
1080            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1081                    "applyAnimation:"
1082                            + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
1083                            + " Callers=" + Debug.getCallers(3));
1084        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
1085            a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
1086            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1087                    "applyAnimation:"
1088                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
1089                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1090                    + " Callers=" + Debug.getCallers(3));
1091        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
1092                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
1093            mNextAppTransitionScaleUp =
1094                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
1095            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
1096                    appWidth, appHeight, transit, taskId);
1097            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1098                String animName = mNextAppTransitionScaleUp ?
1099                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
1100                Slog.v(TAG, "applyAnimation:"
1101                        + " anim=" + a + " nextAppTransition=" + animName
1102                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1103                        + " Callers=" + Debug.getCallers(3));
1104            }
1105        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
1106                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
1107            mNextAppTransitionScaleUp =
1108                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
1109            a = createAspectScaledThumbnailEnterExitAnimationLocked(
1110                    getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit,
1111                    containingFrame, contentInsets, surfaceInsets, resizedWindow, taskId);
1112            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1113                String animName = mNextAppTransitionScaleUp ?
1114                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
1115                Slog.v(TAG, "applyAnimation:"
1116                        + " anim=" + a + " nextAppTransition=" + animName
1117                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1118                        + " Callers=" + Debug.getCallers(3));
1119            }
1120        } else {
1121            int animAttr = 0;
1122            switch (transit) {
1123                case TRANSIT_ACTIVITY_OPEN:
1124                    animAttr = enter
1125                            ? WindowAnimation_activityOpenEnterAnimation
1126                            : WindowAnimation_activityOpenExitAnimation;
1127                    break;
1128                case TRANSIT_ACTIVITY_CLOSE:
1129                    animAttr = enter
1130                            ? WindowAnimation_activityCloseEnterAnimation
1131                            : WindowAnimation_activityCloseExitAnimation;
1132                    break;
1133                case TRANSIT_TASK_OPEN:
1134                    animAttr = enter
1135                            ? WindowAnimation_taskOpenEnterAnimation
1136                            : WindowAnimation_taskOpenExitAnimation;
1137                    break;
1138                case TRANSIT_TASK_CLOSE:
1139                    animAttr = enter
1140                            ? WindowAnimation_taskCloseEnterAnimation
1141                            : WindowAnimation_taskCloseExitAnimation;
1142                    break;
1143                case TRANSIT_TASK_TO_FRONT:
1144                    animAttr = enter
1145                            ? WindowAnimation_taskToFrontEnterAnimation
1146                            : WindowAnimation_taskToFrontExitAnimation;
1147                    break;
1148                case TRANSIT_TASK_TO_BACK:
1149                    animAttr = enter
1150                            ? WindowAnimation_taskToBackEnterAnimation
1151                            : WindowAnimation_taskToBackExitAnimation;
1152                    break;
1153                case TRANSIT_WALLPAPER_OPEN:
1154                    animAttr = enter
1155                            ? WindowAnimation_wallpaperOpenEnterAnimation
1156                            : WindowAnimation_wallpaperOpenExitAnimation;
1157                    break;
1158                case TRANSIT_WALLPAPER_CLOSE:
1159                    animAttr = enter
1160                            ? WindowAnimation_wallpaperCloseEnterAnimation
1161                            : WindowAnimation_wallpaperCloseExitAnimation;
1162                    break;
1163                case TRANSIT_WALLPAPER_INTRA_OPEN:
1164                    animAttr = enter
1165                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
1166                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
1167                    break;
1168                case TRANSIT_WALLPAPER_INTRA_CLOSE:
1169                    animAttr = enter
1170                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
1171                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
1172                    break;
1173                case TRANSIT_TASK_OPEN_BEHIND:
1174                    animAttr = enter
1175                            ? WindowAnimation_launchTaskBehindSourceAnimation
1176                            : WindowAnimation_launchTaskBehindTargetAnimation;
1177            }
1178            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
1179            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1180                    "applyAnimation:"
1181                    + " anim=" + a
1182                    + " animAttr=0x" + Integer.toHexString(animAttr)
1183                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1184                    + " Callers=" + Debug.getCallers(3));
1185        }
1186        return a;
1187    }
1188
1189    void postAnimationCallback() {
1190        if (mNextAppTransitionCallback != null) {
1191            mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback));
1192            mNextAppTransitionCallback = null;
1193        }
1194    }
1195
1196    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
1197            IRemoteCallback startedCallback) {
1198        if (isTransitionSet()) {
1199            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
1200            mNextAppTransitionPackage = packageName;
1201            mNextAppTransitionAnimationsSpecs.clear();
1202            mNextAppTransitionEnter = enterAnim;
1203            mNextAppTransitionExit = exitAnim;
1204            postAnimationCallback();
1205            mNextAppTransitionCallback = startedCallback;
1206        } else {
1207            postAnimationCallback();
1208        }
1209    }
1210
1211    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
1212            int startHeight) {
1213        if (isTransitionSet()) {
1214            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
1215            mNextAppTransitionPackage = null;
1216            mNextAppTransitionAnimationsSpecs.clear();
1217            putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
1218                    startY + startHeight);
1219            postAnimationCallback();
1220            mNextAppTransitionCallback = null;
1221        }
1222    }
1223
1224    void overridePendingAppTransitionClipReveal(int startX, int startY,
1225                                                int startWidth, int startHeight) {
1226        if (isTransitionSet()) {
1227            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1228            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight);
1229            postAnimationCallback();
1230            mNextAppTransitionCallback = null;
1231        }
1232    }
1233
1234    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
1235                                           IRemoteCallback startedCallback, boolean scaleUp) {
1236        if (isTransitionSet()) {
1237            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
1238                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
1239            mNextAppTransitionPackage = null;
1240            mNextAppTransitionAnimationsSpecs.clear();
1241            mNextAppTransitionScaleUp = scaleUp;
1242            putDefaultNextAppTransitionCoordinates(startX, startY, 0 ,0);
1243            postAnimationCallback();
1244            mNextAppTransitionCallback = startedCallback;
1245        } else {
1246            postAnimationCallback();
1247        }
1248    }
1249
1250    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
1251            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
1252        if (isTransitionSet()) {
1253            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1254                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1255            mNextAppTransitionPackage = null;
1256            mNextAppTransitionAnimationsSpecs.clear();
1257            mNextAppTransitionScaleUp = scaleUp;
1258            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight);
1259            postAnimationCallback();
1260            mNextAppTransitionCallback = startedCallback;
1261        } else {
1262            postAnimationCallback();
1263        }
1264    }
1265
1266    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
1267            IRemoteCallback callback, boolean scaleUp) {
1268        if (isTransitionSet()) {
1269            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1270                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1271            mNextAppTransitionPackage = null;
1272            mDefaultNextAppTransitionAnimationSpec = null;
1273            mNextAppTransitionAnimationsSpecs.clear();
1274            mNextAppTransitionScaleUp = scaleUp;
1275            for (int i = 0; i < specs.length; i++) {
1276                AppTransitionAnimationSpec spec = specs[i];
1277                if (spec != null) {
1278                    mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
1279                    if (i == 0) {
1280                        // In full screen mode, the transition code depends on the default spec to
1281                        // be set.
1282                        Rect rect = spec.rect;
1283                        putDefaultNextAppTransitionCoordinates(rect.left, rect.top, rect.width(),
1284                                rect.height());
1285                    }
1286                }
1287            }
1288            postAnimationCallback();
1289            mNextAppTransitionCallback = callback;
1290        } else {
1291            postAnimationCallback();
1292        }
1293    }
1294
1295    void overrideInPlaceAppTransition(String packageName, int anim) {
1296        if (isTransitionSet()) {
1297            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
1298            mNextAppTransitionPackage = packageName;
1299            mNextAppTransitionInPlace = anim;
1300        } else {
1301            postAnimationCallback();
1302        }
1303    }
1304
1305    @Override
1306    public String toString() {
1307        return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
1308    }
1309
1310    /**
1311     * Returns the human readable name of a window transition.
1312     *
1313     * @param transition The window transition.
1314     * @return The transition symbolic name.
1315     */
1316    public static String appTransitionToString(int transition) {
1317        switch (transition) {
1318            case TRANSIT_UNSET: {
1319                return "TRANSIT_UNSET";
1320            }
1321            case TRANSIT_NONE: {
1322                return "TRANSIT_NONE";
1323            }
1324            case TRANSIT_ACTIVITY_OPEN: {
1325                return "TRANSIT_ACTIVITY_OPEN";
1326            }
1327            case TRANSIT_ACTIVITY_CLOSE: {
1328                return "TRANSIT_ACTIVITY_CLOSE";
1329            }
1330            case TRANSIT_TASK_OPEN: {
1331                return "TRANSIT_TASK_OPEN";
1332            }
1333            case TRANSIT_TASK_CLOSE: {
1334                return "TRANSIT_TASK_CLOSE";
1335            }
1336            case TRANSIT_TASK_TO_FRONT: {
1337                return "TRANSIT_TASK_TO_FRONT";
1338            }
1339            case TRANSIT_TASK_TO_BACK: {
1340                return "TRANSIT_TASK_TO_BACK";
1341            }
1342            case TRANSIT_WALLPAPER_CLOSE: {
1343                return "TRANSIT_WALLPAPER_CLOSE";
1344            }
1345            case TRANSIT_WALLPAPER_OPEN: {
1346                return "TRANSIT_WALLPAPER_OPEN";
1347            }
1348            case TRANSIT_WALLPAPER_INTRA_OPEN: {
1349                return "TRANSIT_WALLPAPER_INTRA_OPEN";
1350            }
1351            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1352                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1353            }
1354            case TRANSIT_TASK_OPEN_BEHIND: {
1355                return "TRANSIT_TASK_OPEN_BEHIND";
1356            }
1357            case TRANSIT_ACTIVITY_RELAUNCH: {
1358                return "TRANSIT_ACTIVITY_RELAUNCH";
1359            }
1360            default: {
1361                return "<UNKNOWN>";
1362            }
1363        }
1364    }
1365
1366    private String appStateToString() {
1367        switch (mAppTransitionState) {
1368            case APP_STATE_IDLE:
1369                return "APP_STATE_IDLE";
1370            case APP_STATE_READY:
1371                return "APP_STATE_READY";
1372            case APP_STATE_RUNNING:
1373                return "APP_STATE_RUNNING";
1374            case APP_STATE_TIMEOUT:
1375                return "APP_STATE_TIMEOUT";
1376            default:
1377                return "unknown state=" + mAppTransitionState;
1378        }
1379    }
1380
1381    private String transitTypeToString() {
1382        switch (mNextAppTransitionType) {
1383            case NEXT_TRANSIT_TYPE_NONE:
1384                return "NEXT_TRANSIT_TYPE_NONE";
1385            case NEXT_TRANSIT_TYPE_CUSTOM:
1386                return "NEXT_TRANSIT_TYPE_CUSTOM";
1387            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1388                return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE";
1389            case NEXT_TRANSIT_TYPE_SCALE_UP:
1390                return "NEXT_TRANSIT_TYPE_SCALE_UP";
1391            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1392                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1393            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1394                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1395            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1396                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1397            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1398                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1399            default:
1400                return "unknown type=" + mNextAppTransitionType;
1401        }
1402    }
1403
1404    @Override
1405    public void dump(PrintWriter pw, String prefix) {
1406        pw.print(prefix); pw.println(this);
1407        pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString());
1408        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1409            pw.print(prefix); pw.print("mNextAppTransitionType=");
1410                    pw.println(transitTypeToString());
1411        }
1412        switch (mNextAppTransitionType) {
1413            case NEXT_TRANSIT_TYPE_CUSTOM:
1414                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1415                        pw.println(mNextAppTransitionPackage);
1416                pw.print(prefix); pw.print("mNextAppTransitionEnter=0x");
1417                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
1418                        pw.print(" mNextAppTransitionExit=0x");
1419                        pw.println(Integer.toHexString(mNextAppTransitionExit));
1420                break;
1421            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1422                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1423                        pw.println(mNextAppTransitionPackage);
1424                pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
1425                        pw.print(Integer.toHexString(mNextAppTransitionInPlace));
1426                break;
1427            case NEXT_TRANSIT_TYPE_SCALE_UP: {
1428                getDefaultNextAppTransitionStartRect(mTmpStartRect);
1429                pw.print(prefix); pw.print("mNextAppTransitionStartX=");
1430                        pw.print(mTmpStartRect.left);
1431                        pw.print(" mNextAppTransitionStartY=");
1432                        pw.println(mTmpStartRect.top);
1433                pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
1434                        pw.print(mTmpStartRect.width());
1435                        pw.print(" mNextAppTransitionStartHeight=");
1436                        pw.println(mTmpStartRect.height());
1437                break;
1438            }
1439            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1440            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1441            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1442            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
1443                pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
1444                        pw.println(mDefaultNextAppTransitionAnimationSpec);
1445                pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
1446                        pw.println(mNextAppTransitionAnimationsSpecs);
1447                pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
1448                        pw.println(mNextAppTransitionScaleUp);
1449                break;
1450            }
1451        }
1452        if (mNextAppTransitionCallback != null) {
1453            pw.print(prefix); pw.print("mNextAppTransitionCallback=");
1454                    pw.println(mNextAppTransitionCallback);
1455        }
1456    }
1457
1458    public void setCurrentUser(int newUserId) {
1459        mCurrentUserId = newUserId;
1460    }
1461}
1462