AppTransition.java revision 363ab98fced07bf7647355367be9e6ef76751450
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 static android.view.WindowManagerInternal.AppTransitionListener;
20import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation;
21import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
22import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
23import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
24import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
25import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
26import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
27import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
28import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation;
29import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
30import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation;
31import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
32import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation;
33import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
34import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation;
35import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
36import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation;
37import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
38import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation;
39import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
40import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
41import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
42import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
43import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
44import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
45import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
46import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
47import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
48import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
49
50import android.annotation.Nullable;
51import android.content.Context;
52import android.content.res.Configuration;
53import android.graphics.Bitmap;
54import android.graphics.Path;
55import android.graphics.Rect;
56import android.os.Debug;
57import android.os.IBinder;
58import android.os.IRemoteCallback;
59import android.os.RemoteException;
60import android.util.ArraySet;
61import android.util.Slog;
62import android.util.SparseArray;
63import android.view.AppTransitionAnimationSpec;
64import android.view.IAppTransitionAnimationSpecsFuture;
65import android.view.WindowManager;
66import android.view.animation.AlphaAnimation;
67import android.view.animation.Animation;
68import android.view.animation.AnimationSet;
69import android.view.animation.AnimationUtils;
70import android.view.animation.ClipRectAnimation;
71import android.view.animation.Interpolator;
72import android.view.animation.PathInterpolator;
73import android.view.animation.ScaleAnimation;
74import android.view.animation.TranslateAnimation;
75
76import com.android.internal.util.DumpUtils.Dump;
77import com.android.server.AttributeCache;
78import com.android.server.wm.WindowManagerService.H;
79import com.android.server.wm.animation.ClipRectLRAnimation;
80import com.android.server.wm.animation.ClipRectTBAnimation;
81import com.android.server.wm.animation.CurvedTranslateAnimation;
82
83import java.io.PrintWriter;
84import java.util.ArrayList;
85import java.util.concurrent.ExecutorService;
86import java.util.concurrent.Executors;
87
88// State management of app transitions.  When we are preparing for a
89// transition, mNextAppTransition will be the kind of transition to
90// perform or TRANSIT_NONE if we are not waiting.  If we are waiting,
91// mOpeningApps and mClosingApps are the lists of tokens that will be
92// made visible or hidden at the next transition.
93public class AppTransition implements Dump {
94    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM;
95    private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8;
96
97    /** Not set up for a transition. */
98    public static final int TRANSIT_UNSET = -1;
99    /** No animation for transition. */
100    public static final int TRANSIT_NONE = 0;
101    /** A window in a new activity is being opened on top of an existing one in the same task. */
102    public static final int TRANSIT_ACTIVITY_OPEN = 6;
103    /** The window in the top-most activity is being closed to reveal the
104     * previous activity in the same task. */
105    public static final int TRANSIT_ACTIVITY_CLOSE = 7;
106    /** A window in a new task is being opened on top of an existing one
107     * in another activity's task. */
108    public static final int TRANSIT_TASK_OPEN = 8;
109    /** A window in the top-most activity is being closed to reveal the
110     * previous activity in a different task. */
111    public static final int TRANSIT_TASK_CLOSE = 9;
112    /** A window in an existing task is being displayed on top of an existing one
113     * in another activity's task. */
114    public static final int TRANSIT_TASK_TO_FRONT = 10;
115    /** A window in an existing task is being put below all other tasks. */
116    public static final int TRANSIT_TASK_TO_BACK = 11;
117    /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that
118     * does, effectively closing the wallpaper. */
119    public static final int TRANSIT_WALLPAPER_CLOSE = 12;
120    /** A window in a new activity that does have a wallpaper is being opened on one that didn't,
121     * effectively opening the wallpaper. */
122    public static final int TRANSIT_WALLPAPER_OPEN = 13;
123    /** A window in a new activity is being opened on top of an existing one, and both are on top
124     * of the wallpaper. */
125    public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
126    /** The window in the top-most activity is being closed to reveal the previous activity, and
127     * both are on top of the wallpaper. */
128    public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
129    /** A window in a new task is being opened behind an existing one in another activity's task.
130     * The new window will show briefly and then be gone. */
131    public static final int TRANSIT_TASK_OPEN_BEHIND = 16;
132    /** A window in a task is being animated in-place. */
133    public static final int TRANSIT_TASK_IN_PLACE = 17;
134    /** An activity is being relaunched (e.g. due to configuration change). */
135    public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
136    /** A task is being docked from recents. */
137    public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
138
139    /** Fraction of animation at which the recents thumbnail stays completely transparent */
140    private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
141    /** Fraction of animation at which the recents thumbnail becomes completely transparent */
142    private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
143
144    static final int DEFAULT_APP_TRANSITION_DURATION = 336;
145
146    /** Interpolator to be used for animations that respond directly to a touch */
147    static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
148            new PathInterpolator(0.3f, 0f, 0.1f, 1f);
149
150    private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
151            new PathInterpolator(0.85f, 0f, 1f, 1f);
152
153    /**
154     * Maximum duration for the clip reveal animation. This is used when there is a lot of movement
155     * involved, to make it more understandable.
156     */
157    private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
158    private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
159    private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
160
161    private final Context mContext;
162    private final WindowManagerService mService;
163
164    private int mNextAppTransition = TRANSIT_UNSET;
165
166    private static final int NEXT_TRANSIT_TYPE_NONE = 0;
167    private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1;
168    private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2;
169    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3;
170    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
171    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5;
172    private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6;
173    private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7;
174    private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8;
175    private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
176
177    // These are the possible states for the enter/exit activities during a thumbnail transition
178    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
179    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
180    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
181    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
182
183    private String mNextAppTransitionPackage;
184    // Used for thumbnail transitions. True if we're scaling up, false if scaling down
185    private boolean mNextAppTransitionScaleUp;
186    private IRemoteCallback mNextAppTransitionCallback;
187    private IRemoteCallback mNextAppTransitionFutureCallback;
188    private IRemoteCallback mAnimationFinishedCallback;
189    private int mNextAppTransitionEnter;
190    private int mNextAppTransitionExit;
191    private int mNextAppTransitionInPlace;
192
193    // Keyed by task id.
194    private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
195            = new SparseArray<>();
196    private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture;
197    private boolean mNextAppTransitionAnimationsSpecsPending;
198    private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec;
199
200    private Rect mNextAppTransitionInsets = new Rect();
201
202    private Rect mTmpFromClipRect = new Rect();
203    private Rect mTmpToClipRect = new Rect();
204
205    private final Rect mTmpRect = new Rect();
206
207    private final static int APP_STATE_IDLE = 0;
208    private final static int APP_STATE_READY = 1;
209    private final static int APP_STATE_RUNNING = 2;
210    private final static int APP_STATE_TIMEOUT = 3;
211    private int mAppTransitionState = APP_STATE_IDLE;
212
213    private final int mConfigShortAnimTime;
214    private final Interpolator mDecelerateInterpolator;
215    private final Interpolator mThumbnailFadeInInterpolator;
216    private final Interpolator mThumbnailFadeOutInterpolator;
217    private final Interpolator mLinearOutSlowInInterpolator;
218    private final Interpolator mFastOutLinearInInterpolator;
219    private final Interpolator mFastOutSlowInInterpolator;
220    private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
221
222    private final int mClipRevealTranslationY;
223
224    private int mCurrentUserId = 0;
225    private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
226
227    private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
228    private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
229
230    private int mLastClipRevealMaxTranslation;
231    private boolean mLastHadClipReveal;
232    private boolean mProlongedAnimationsEnded;
233
234    AppTransition(Context context, WindowManagerService service) {
235        mContext = context;
236        mService = service;
237        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
238                com.android.internal.R.interpolator.linear_out_slow_in);
239        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
240                com.android.internal.R.interpolator.fast_out_linear_in);
241        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
242                com.android.internal.R.interpolator.fast_out_slow_in);
243        mConfigShortAnimTime = context.getResources().getInteger(
244                com.android.internal.R.integer.config_shortAnimTime);
245        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
246                com.android.internal.R.interpolator.decelerate_cubic);
247        mThumbnailFadeInInterpolator = new Interpolator() {
248            @Override
249            public float getInterpolation(float input) {
250                // Linear response for first fraction, then complete after that.
251                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
252                    return 0f;
253                }
254                float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
255                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
256                return mFastOutLinearInInterpolator.getInterpolation(t);
257            }
258        };
259        mThumbnailFadeOutInterpolator = new Interpolator() {
260            @Override
261            public float getInterpolation(float input) {
262                // Linear response for first fraction, then complete after that.
263                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
264                    float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
265                    return mLinearOutSlowInInterpolator.getInterpolation(t);
266                }
267                return 1f;
268            }
269        };
270        mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
271                * mContext.getResources().getDisplayMetrics().density);
272    }
273
274    boolean isTransitionSet() {
275        return mNextAppTransition != TRANSIT_UNSET;
276    }
277
278    boolean isTransitionEqual(int transit) {
279        return mNextAppTransition == transit;
280    }
281
282    int getAppTransition() {
283        return mNextAppTransition;
284     }
285
286    private void setAppTransition(int transit) {
287        mNextAppTransition = transit;
288    }
289
290    boolean isReady() {
291        return mAppTransitionState == APP_STATE_READY
292                || mAppTransitionState == APP_STATE_TIMEOUT;
293    }
294
295    void setReady() {
296        mAppTransitionState = APP_STATE_READY;
297        fetchAppTransitionSpecsFromFuture();
298    }
299
300    boolean isRunning() {
301        return mAppTransitionState == APP_STATE_RUNNING;
302    }
303
304    void setIdle() {
305        mAppTransitionState = APP_STATE_IDLE;
306    }
307
308    boolean isTimeout() {
309        return mAppTransitionState == APP_STATE_TIMEOUT;
310    }
311
312    void setTimeout() {
313        mAppTransitionState = APP_STATE_TIMEOUT;
314    }
315
316    Bitmap getAppTransitionThumbnailHeader(int taskId) {
317        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
318        if (spec == null) {
319            spec = mDefaultNextAppTransitionAnimationSpec;
320        }
321        return spec != null ? spec.bitmap : null;
322    }
323
324    /** Returns whether the next thumbnail transition is aspect scaled up. */
325    boolean isNextThumbnailTransitionAspectScaled() {
326        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
327                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
328    }
329
330    /** Returns whether the next thumbnail transition is scaling up. */
331    boolean isNextThumbnailTransitionScaleUp() {
332        return mNextAppTransitionScaleUp;
333    }
334
335    boolean isNextAppTransitionThumbnailUp() {
336        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
337                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP;
338    }
339
340    boolean isNextAppTransitionThumbnailDown() {
341        return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN ||
342                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
343    }
344
345    /**
346     * @return true if and only if we are currently fetching app transition specs from the future
347     *         passed into {@link #overridePendingAppTransitionMultiThumbFuture}
348     */
349    boolean isFetchingAppTransitionsSpecs() {
350        return mNextAppTransitionAnimationsSpecsPending;
351    }
352
353    private boolean prepare() {
354        if (!isRunning()) {
355            mAppTransitionState = APP_STATE_IDLE;
356            notifyAppTransitionPendingLocked();
357            mLastHadClipReveal = false;
358            mLastClipRevealMaxTranslation = 0;
359            mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
360            return true;
361        }
362        return false;
363    }
364
365    void goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator,
366            ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps) {
367        mNextAppTransition = TRANSIT_UNSET;
368        mAppTransitionState = APP_STATE_RUNNING;
369        notifyAppTransitionStartingLocked(
370                topOpeningAppAnimator != null ? topOpeningAppAnimator.mAppToken.token : null,
371                topClosingAppAnimator != null ? topClosingAppAnimator.mAppToken.token : null,
372                topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null,
373                topClosingAppAnimator != null ? topClosingAppAnimator.animation : null);
374        mService.getDefaultDisplayContentLocked().getDockedDividerController()
375                .notifyAppTransitionStarting(openingApps, closingApps);
376
377        // Prolong the start for the transition when docking a task from recents, unless recents
378        // ended it already then we don't need to wait.
379        if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS && !mProlongedAnimationsEnded) {
380            for (int i = openingApps.size() - 1; i >= 0; i--) {
381                final AppWindowAnimator appAnimator = openingApps.valueAt(i).mAppAnimator;
382                appAnimator.startProlongAnimation(PROLONG_ANIMATION_AT_START);
383            }
384        }
385    }
386
387    /**
388     * Let the transitions manager know that the somebody wanted to end the prolonged animations.
389     */
390    void notifyProlongedAnimationsEnded() {
391        mProlongedAnimationsEnded = true;
392    }
393
394    void clear() {
395        mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
396        mNextAppTransitionPackage = null;
397        mNextAppTransitionAnimationsSpecs.clear();
398        mNextAppTransitionAnimationsSpecsFuture = null;
399        mDefaultNextAppTransitionAnimationSpec = null;
400        mAnimationFinishedCallback = null;
401        mProlongedAnimationsEnded = false;
402    }
403
404    void freeze() {
405        setAppTransition(AppTransition.TRANSIT_UNSET);
406        clear();
407        setReady();
408        notifyAppTransitionCancelledLocked();
409    }
410
411    void registerListenerLocked(AppTransitionListener listener) {
412        mListeners.add(listener);
413    }
414
415    public void notifyAppTransitionFinishedLocked(IBinder token) {
416        for (int i = 0; i < mListeners.size(); i++) {
417            mListeners.get(i).onAppTransitionFinishedLocked(token);
418        }
419    }
420
421    private void notifyAppTransitionPendingLocked() {
422        for (int i = 0; i < mListeners.size(); i++) {
423            mListeners.get(i).onAppTransitionPendingLocked();
424        }
425    }
426
427    private void notifyAppTransitionCancelledLocked() {
428        for (int i = 0; i < mListeners.size(); i++) {
429            mListeners.get(i).onAppTransitionCancelledLocked();
430        }
431    }
432
433    private void notifyAppTransitionStartingLocked(IBinder openToken,
434            IBinder closeToken, Animation openAnimation, Animation closeAnimation) {
435        for (int i = 0; i < mListeners.size(); i++) {
436            mListeners.get(i).onAppTransitionStartingLocked(openToken, closeToken, openAnimation,
437                    closeAnimation);
438        }
439    }
440
441    private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) {
442        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
443                + (lp != null ? lp.packageName : null)
444                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
445        if (lp != null && lp.windowAnimations != 0) {
446            // If this is a system resource, don't try to load it from the
447            // application resources.  It is nice to avoid loading application
448            // resources if we can.
449            String packageName = lp.packageName != null ? lp.packageName : "android";
450            int resId = lp.windowAnimations;
451            if ((resId&0xFF000000) == 0x01000000) {
452                packageName = "android";
453            }
454            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
455                    + packageName);
456            return AttributeCache.instance().get(packageName, resId,
457                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
458        }
459        return null;
460    }
461
462    private AttributeCache.Entry getCachedAnimations(String packageName, int resId) {
463        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package="
464                + packageName + " resId=0x" + Integer.toHexString(resId));
465        if (packageName != null) {
466            if ((resId&0xFF000000) == 0x01000000) {
467                packageName = "android";
468            }
469            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
470                    + packageName);
471            return AttributeCache.instance().get(packageName, resId,
472                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
473        }
474        return null;
475    }
476
477    Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) {
478        int anim = 0;
479        Context context = mContext;
480        if (animAttr >= 0) {
481            AttributeCache.Entry ent = getCachedAnimations(lp);
482            if (ent != null) {
483                context = ent.context;
484                anim = ent.array.getResourceId(animAttr, 0);
485            }
486        }
487        if (anim != 0) {
488            return AnimationUtils.loadAnimation(context, anim);
489        }
490        return null;
491    }
492
493    Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) {
494        Context context = mContext;
495        if (resId >= 0) {
496            AttributeCache.Entry ent = getCachedAnimations(lp);
497            if (ent != null) {
498                context = ent.context;
499            }
500            return AnimationUtils.loadAnimation(context, resId);
501        }
502        return null;
503    }
504
505    private Animation loadAnimationRes(String packageName, int resId) {
506        int anim = 0;
507        Context context = mContext;
508        if (resId >= 0) {
509            AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
510            if (ent != null) {
511                context = ent.context;
512                anim = resId;
513            }
514        }
515        if (anim != 0) {
516            return AnimationUtils.loadAnimation(context, anim);
517        }
518        return null;
519    }
520
521    /**
522     * Compute the pivot point for an animation that is scaling from a small
523     * rect on screen to a larger rect.  The pivot point varies depending on
524     * the distance between the inner and outer edges on both sides.  This
525     * function computes the pivot point for one dimension.
526     * @param startPos  Offset from left/top edge of outer rectangle to
527     * left/top edge of inner rectangle.
528     * @param finalScale The scaling factor between the size of the outer
529     * and inner rectangles.
530     */
531    private static float computePivot(int startPos, float finalScale) {
532
533        /*
534        Theorem of intercepting lines:
535
536          +      +   +-----------------------------------------------+
537          |      |   |                                               |
538          |      |   |                                               |
539          |      |   |                                               |
540          |      |   |                                               |
541        x |    y |   |                                               |
542          |      |   |                                               |
543          |      |   |                                               |
544          |      |   |                                               |
545          |      |   |                                               |
546          |      +   |             +--------------------+            |
547          |          |             |                    |            |
548          |          |             |                    |            |
549          |          |             |                    |            |
550          |          |             |                    |            |
551          |          |             |                    |            |
552          |          |             |                    |            |
553          |          |             |                    |            |
554          |          |             |                    |            |
555          |          |             |                    |            |
556          |          |             |                    |            |
557          |          |             |                    |            |
558          |          |             |                    |            |
559          |          |             |                    |            |
560          |          |             |                    |            |
561          |          |             |                    |            |
562          |          |             |                    |            |
563          |          |             |                    |            |
564          |          |             +--------------------+            |
565          |          |                                               |
566          |          |                                               |
567          |          |                                               |
568          |          |                                               |
569          |          |                                               |
570          |          |                                               |
571          |          |                                               |
572          |          +-----------------------------------------------+
573          |
574          |
575          |
576          |
577          |
578          |
579          |
580          |
581          |
582          +                                 ++
583                                         p  ++
584
585        scale = (x - y) / x
586        <=> x = -y / (scale - 1)
587        */
588        final float denom = finalScale-1;
589        if (Math.abs(denom) < .0001f) {
590            return startPos;
591        }
592        return -startPos / denom;
593    }
594
595    private Animation createScaleUpAnimationLocked(int transit, boolean enter,
596            Rect containingFrame) {
597        Animation a;
598        getDefaultNextAppTransitionStartRect(mTmpRect);
599        final int appWidth = containingFrame.width();
600        final int appHeight = containingFrame.height();
601        if (enter) {
602            // Entering app zooms out from the center of the initial rect.
603            float scaleW = mTmpRect.width() / (float) appWidth;
604            float scaleH = mTmpRect.height() / (float) appHeight;
605            Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
606                    computePivot(mTmpRect.left, scaleW),
607                    computePivot(mTmpRect.right, scaleH));
608            scale.setInterpolator(mDecelerateInterpolator);
609
610            Animation alpha = new AlphaAnimation(0, 1);
611            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
612
613            AnimationSet set = new AnimationSet(false);
614            set.addAnimation(scale);
615            set.addAnimation(alpha);
616            set.setDetachWallpaper(true);
617            a = set;
618        } else  if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
619                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
620            // If we are on top of the wallpaper, we need an animation that
621            // correctly handles the wallpaper staying static behind all of
622            // the animated elements.  To do this, will just have the existing
623            // element fade out.
624            a = new AlphaAnimation(1, 0);
625            a.setDetachWallpaper(true);
626        } else {
627            // For normal animations, the exiting element just holds in place.
628            a = new AlphaAnimation(1, 1);
629        }
630
631        // Pick the desired duration.  If this is an inter-activity transition,
632        // it  is the standard duration for that.  Otherwise we use the longer
633        // task transition duration.
634        final long duration;
635        switch (transit) {
636            case TRANSIT_ACTIVITY_OPEN:
637            case TRANSIT_ACTIVITY_CLOSE:
638                duration = mConfigShortAnimTime;
639                break;
640            default:
641                duration = DEFAULT_APP_TRANSITION_DURATION;
642                break;
643        }
644        a.setDuration(duration);
645        a.setFillAfter(true);
646        a.setInterpolator(mDecelerateInterpolator);
647        a.initialize(appWidth, appHeight, appWidth, appHeight);
648        return a;
649    }
650
651    private void getDefaultNextAppTransitionStartRect(Rect rect) {
652        if (mDefaultNextAppTransitionAnimationSpec == null ||
653                mDefaultNextAppTransitionAnimationSpec.rect == null) {
654            Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
655            rect.setEmpty();
656        } else {
657            rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
658        }
659    }
660
661    void getNextAppTransitionStartRect(int taskId, Rect rect) {
662        AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId);
663        if (spec == null) {
664            spec = mDefaultNextAppTransitionAnimationSpec;
665        }
666        if (spec == null || spec.rect == null) {
667            Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
668                    new Throwable());
669            rect.setEmpty();
670        } else {
671            rect.set(spec.rect);
672        }
673    }
674
675    private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height,
676            Bitmap bitmap) {
677        mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */,
678                bitmap, new Rect(left, top, left + width, top + height));
679    }
680
681    /**
682     * @return the duration of the last clip reveal animation
683     */
684    long getLastClipRevealTransitionDuration() {
685        return mLastClipRevealTransitionDuration;
686    }
687
688    /**
689     * @return the maximum distance the app surface is traveling of the last clip reveal animation
690     */
691    int getLastClipRevealMaxTranslation() {
692        return mLastClipRevealMaxTranslation;
693    }
694
695    /**
696     * @return true if in the last app transition had a clip reveal animation, false otherwise
697     */
698    boolean hadClipRevealAnimation() {
699        return mLastHadClipReveal;
700    }
701
702    /**
703     * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
704     * the start rect is outside of the target rect, and there is a lot of movement going on.
705     *
706     * @param cutOff whether the start rect was not fully contained by the end rect
707     * @param translationX the total translation the surface moves in x direction
708     * @param translationY the total translation the surfaces moves in y direction
709     * @param displayFrame our display frame
710     *
711     * @return the duration of the clip reveal animation, in milliseconds
712     */
713    private long calculateClipRevealTransitionDuration(boolean cutOff, float translationX,
714            float translationY, Rect displayFrame) {
715        if (!cutOff) {
716            return DEFAULT_APP_TRANSITION_DURATION;
717        }
718        final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(),
719                Math.abs(translationY) / displayFrame.height());
720        return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction *
721                (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION));
722    }
723
724    private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
725            Rect displayFrame) {
726        final Animation anim;
727        if (enter) {
728            final int appWidth = appFrame.width();
729            final int appHeight = appFrame.height();
730
731            // mTmpRect will contain an area around the launcher icon that was pressed. We will
732            // clip reveal from that area in the final area of the app.
733            getDefaultNextAppTransitionStartRect(mTmpRect);
734
735            float t = 0f;
736            if (appHeight > 0) {
737                t = (float) mTmpRect.top / displayFrame.height();
738            }
739            int translationY = mClipRevealTranslationY + (int)(displayFrame.height() / 7f * t);
740            int translationX = 0;
741            int translationYCorrection = translationY;
742            int centerX = mTmpRect.centerX();
743            int centerY = mTmpRect.centerY();
744            int halfWidth = mTmpRect.width() / 2;
745            int halfHeight = mTmpRect.height() / 2;
746            int clipStartX = centerX - halfWidth - appFrame.left;
747            int clipStartY = centerY - halfHeight - appFrame.top;
748            boolean cutOff = false;
749
750            // If the starting rectangle is fully or partially outside of the target rectangle, we
751            // need to start the clipping at the edge and then achieve the rest with translation
752            // and extending the clip rect from that edge.
753            if (appFrame.top > centerY - halfHeight) {
754                translationY = (centerY - halfHeight) - appFrame.top;
755                translationYCorrection = 0;
756                clipStartY = 0;
757                cutOff = true;
758            }
759            if (appFrame.left > centerX - halfWidth) {
760                translationX = (centerX - halfWidth) - appFrame.left;
761                clipStartX = 0;
762                cutOff = true;
763            }
764            if (appFrame.right < centerX + halfWidth) {
765                translationX = (centerX + halfWidth) - appFrame.right;
766                clipStartX = appWidth - mTmpRect.width();
767                cutOff = true;
768            }
769            final long duration = calculateClipRevealTransitionDuration(cutOff, translationX,
770                    translationY, displayFrame);
771
772            // Clip third of the from size of launch icon, expand to full width/height
773            Animation clipAnimLR = new ClipRectLRAnimation(
774                    clipStartX, clipStartX + mTmpRect.width(), 0, appWidth);
775            clipAnimLR.setInterpolator(mClipHorizontalInterpolator);
776            clipAnimLR.setDuration((long) (duration / 2.5f));
777
778            TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0);
779            translate.setInterpolator(cutOff ? TOUCH_RESPONSE_INTERPOLATOR
780                    : mLinearOutSlowInInterpolator);
781            translate.setDuration(duration);
782
783            Animation clipAnimTB = new ClipRectTBAnimation(
784                    clipStartY, clipStartY + mTmpRect.height(),
785                    0, appHeight,
786                    translationYCorrection, 0,
787                    mLinearOutSlowInInterpolator);
788            clipAnimTB.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
789            clipAnimTB.setDuration(duration);
790
791            // Quick fade-in from icon to app window
792            final long alphaDuration = duration / 4;
793            AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
794            alpha.setDuration(alphaDuration);
795            alpha.setInterpolator(mLinearOutSlowInInterpolator);
796
797            AnimationSet set = new AnimationSet(false);
798            set.addAnimation(clipAnimLR);
799            set.addAnimation(clipAnimTB);
800            set.addAnimation(translate);
801            set.addAnimation(alpha);
802            set.setZAdjustment(Animation.ZORDER_TOP);
803            set.initialize(appWidth, appHeight, appWidth, appHeight);
804            anim = set;
805            mLastHadClipReveal = true;
806            mLastClipRevealTransitionDuration = duration;
807
808            // If the start rect was full inside the target rect (cutOff == false), we don't need
809            // to store the translation, because it's only used if cutOff == true.
810            mLastClipRevealMaxTranslation = cutOff
811                    ? Math.max(Math.abs(translationY), Math.abs(translationX)) : 0;
812        } else {
813            final long duration;
814            switch (transit) {
815                case TRANSIT_ACTIVITY_OPEN:
816                case TRANSIT_ACTIVITY_CLOSE:
817                    duration = mConfigShortAnimTime;
818                    break;
819                default:
820                    duration = DEFAULT_APP_TRANSITION_DURATION;
821                    break;
822            }
823            if (transit == TRANSIT_WALLPAPER_INTRA_OPEN ||
824                    transit == TRANSIT_WALLPAPER_INTRA_CLOSE) {
825                // If we are on top of the wallpaper, we need an animation that
826                // correctly handles the wallpaper staying static behind all of
827                // the animated elements.  To do this, will just have the existing
828                // element fade out.
829                anim = new AlphaAnimation(1, 0);
830                anim.setDetachWallpaper(true);
831            } else {
832                // For normal animations, the exiting element just holds in place.
833                anim = new AlphaAnimation(1, 1);
834            }
835            anim.setInterpolator(mDecelerateInterpolator);
836            anim.setDuration(duration);
837            anim.setFillAfter(true);
838        }
839        return anim;
840    }
841
842    /**
843     * Prepares the specified animation with a standard duration, interpolator, etc.
844     */
845    Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
846            long duration, Interpolator interpolator) {
847        if (duration > 0) {
848            a.setDuration(duration);
849        }
850        a.setFillAfter(true);
851        if (interpolator != null) {
852            a.setInterpolator(interpolator);
853        }
854        a.initialize(appWidth, appHeight, appWidth, appHeight);
855        return a;
856    }
857
858    /**
859     * Prepares the specified animation with a standard duration, interpolator, etc.
860     */
861    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
862        // Pick the desired duration.  If this is an inter-activity transition,
863        // it  is the standard duration for that.  Otherwise we use the longer
864        // task transition duration.
865        final int duration;
866        switch (transit) {
867            case TRANSIT_ACTIVITY_OPEN:
868            case TRANSIT_ACTIVITY_CLOSE:
869                duration = mConfigShortAnimTime;
870                break;
871            default:
872                duration = DEFAULT_APP_TRANSITION_DURATION;
873                break;
874        }
875        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
876                mDecelerateInterpolator);
877    }
878
879    /**
880     * Return the current thumbnail transition state.
881     */
882    int getThumbnailTransitionState(boolean enter) {
883        if (enter) {
884            if (mNextAppTransitionScaleUp) {
885                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
886            } else {
887                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
888            }
889        } else {
890            if (mNextAppTransitionScaleUp) {
891                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
892            } else {
893                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
894            }
895        }
896    }
897
898    /**
899     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
900     * when a thumbnail is specified with the pending animation override.
901     */
902    Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
903            Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation) {
904        Animation a;
905        final int thumbWidthI = thumbnailHeader.getWidth();
906        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
907        final int thumbHeightI = thumbnailHeader.getHeight();
908        final int appWidth = appRect.width();
909
910        float scaleW = appWidth / thumbWidth;
911        getNextAppTransitionStartRect(taskId, mTmpRect);
912        final float fromX;
913        final float fromY;
914        final float toX;
915        final float toY;
916        final float pivotX;
917        final float pivotY;
918        if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
919            fromX = mTmpRect.left;
920            fromY = mTmpRect.top;
921
922            // For the curved translate animation to work, the pivot points needs to be at the
923            // same absolute position as the one from the real surface.
924            toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
925            toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
926            pivotX = mTmpRect.width() / 2;
927            pivotY = appRect.height() / 2 / scaleW;
928        } else {
929            pivotX = 0;
930            pivotY = 0;
931            fromX = mTmpRect.left;
932            fromY = mTmpRect.top;
933            toX = appRect.left;
934            toY = appRect.top;
935        }
936        final long duration = getAspectScaleDuration();
937        final Interpolator interpolator = getAspectScaleInterpolator();
938        if (mNextAppTransitionScaleUp) {
939            // Animation up from the thumbnail to the full screen
940            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
941            scale.setInterpolator(interpolator);
942            scale.setDuration(duration);
943            Animation alpha = new AlphaAnimation(1f, 0f);
944            alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
945                    ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
946            alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
947                    ? duration / 2
948                    : duration);
949            Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
950            translate.setInterpolator(interpolator);
951            translate.setDuration(duration);
952
953            mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
954            mTmpToClipRect.set(appRect);
955
956            // Containing frame is in screen space, but we need the clip rect in the
957            // app space.
958            mTmpToClipRect.offsetTo(0, 0);
959            mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
960            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
961
962            if (contentInsets != null) {
963                mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
964                        (int) (-contentInsets.top * scaleW),
965                        (int) (-contentInsets.right * scaleW),
966                        (int) (-contentInsets.bottom * scaleW));
967            }
968
969            Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
970            clipAnim.setInterpolator(interpolator);
971            clipAnim.setDuration(duration);
972
973            // This AnimationSet uses the Interpolators assigned above.
974            AnimationSet set = new AnimationSet(false);
975            set.addAnimation(scale);
976            set.addAnimation(alpha);
977            set.addAnimation(translate);
978            set.addAnimation(clipAnim);
979            a = set;
980        } else {
981            // Animation down from the full screen to the thumbnail
982            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
983            scale.setInterpolator(interpolator);
984            scale.setDuration(duration);
985            Animation alpha = new AlphaAnimation(0f, 1f);
986            alpha.setInterpolator(mThumbnailFadeInInterpolator);
987            alpha.setDuration(duration);
988            Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
989            translate.setInterpolator(interpolator);
990            translate.setDuration(duration);
991
992            // This AnimationSet uses the Interpolators assigned above.
993            AnimationSet set = new AnimationSet(false);
994            set.addAnimation(scale);
995            set.addAnimation(alpha);
996            set.addAnimation(translate);
997            a = set;
998
999        }
1000        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
1001                null);
1002    }
1003
1004    private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
1005
1006        // Almost no x-change - use linear animation
1007        if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
1008            return new TranslateAnimation(fromX, toX, fromY, toY);
1009        } else {
1010            final Path path = createCurvedPath(fromX, toX, fromY, toY);
1011            return new CurvedTranslateAnimation(path);
1012        }
1013    }
1014
1015    private Path createCurvedPath(float fromX, float toX, float fromY, float toY) {
1016        final Path path = new Path();
1017        path.moveTo(fromX, fromY);
1018
1019        if (fromY > toY) {
1020            // If the object needs to go up, move it in horizontal direction first, then vertical.
1021            path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY);
1022        } else {
1023            // If the object needs to go down, move it in vertical direction first, then horizontal.
1024            path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY);
1025        }
1026        return path;
1027    }
1028
1029    private long getAspectScaleDuration() {
1030        if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
1031            return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
1032        } else {
1033            return THUMBNAIL_APP_TRANSITION_DURATION;
1034        }
1035    }
1036
1037    private Interpolator getAspectScaleInterpolator() {
1038        if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
1039            return mFastOutSlowInInterpolator;
1040        } else {
1041            return TOUCH_RESPONSE_INTERPOLATOR;
1042        }
1043    }
1044
1045    /**
1046     * This alternate animation is created when we are doing a thumbnail transition, for the
1047     * activity that is leaving, and the activity that is entering.
1048     */
1049    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
1050            int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
1051            @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
1052        Animation a;
1053        final int appWidth = containingFrame.width();
1054        final int appHeight = containingFrame.height();
1055        getDefaultNextAppTransitionStartRect(mTmpRect);
1056        final int thumbWidthI = mTmpRect.width();
1057        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1058        final int thumbHeightI = mTmpRect.height();
1059        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1060        final int thumbStartX = mTmpRect.left - containingFrame.left;
1061        final int thumbStartY = mTmpRect.top - containingFrame.top;
1062
1063        switch (thumbTransitState) {
1064            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
1065            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1066                final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
1067                if (freeform && scaleUp) {
1068                    a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
1069                            containingFrame, surfaceInsets, taskId);
1070                } else if (freeform) {
1071                    a = createAspectScaledThumbnailExitFreeformAnimationLocked(
1072                            containingFrame, surfaceInsets, taskId);
1073                } else {
1074                    AnimationSet set = new AnimationSet(true);
1075
1076                    // In portrait, we scale to fit the width
1077                    mTmpFromClipRect.set(containingFrame);
1078                    mTmpToClipRect.set(containingFrame);
1079
1080                    // Containing frame is in screen space, but we need the clip rect in the
1081                    // app space.
1082                    mTmpFromClipRect.offsetTo(0, 0);
1083                    mTmpToClipRect.offsetTo(0, 0);
1084
1085                    // Exclude insets region from the source clip.
1086                    mTmpFromClipRect.inset(contentInsets);
1087                    mNextAppTransitionInsets.set(contentInsets);
1088
1089                    if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
1090                        // We scale the width and clip to the top/left square
1091                        float scale = thumbWidth /
1092                                (appWidth - contentInsets.left - contentInsets.right);
1093                        int unscaledThumbHeight = (int) (thumbHeight / scale);
1094                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
1095
1096                        mNextAppTransitionInsets.set(contentInsets);
1097
1098                        Animation scaleAnim = new ScaleAnimation(
1099                                scaleUp ? scale : 1, scaleUp ? 1 : scale,
1100                                scaleUp ? scale : 1, scaleUp ? 1 : scale,
1101                                containingFrame.width() / 2f,
1102                                containingFrame.height() / 2f + contentInsets.top);
1103                        final float targetX = (mTmpRect.left - containingFrame.left);
1104                        final float x = containingFrame.width() / 2f
1105                                - containingFrame.width() / 2f * scale;
1106                        final float targetY = (mTmpRect.top - containingFrame.top);
1107                        final float y = containingFrame.height() / 2f
1108                                - containingFrame.height() / 2f * scale;
1109                        final float startX = targetX - x;
1110                        final float startY = targetY - y;
1111                        Animation clipAnim = scaleUp
1112                                ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1113                                : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1114                        Animation translateAnim = scaleUp
1115                                ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
1116                                : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
1117
1118                        set.addAnimation(clipAnim);
1119                        set.addAnimation(scaleAnim);
1120                        set.addAnimation(translateAnim);
1121
1122                    } else {
1123                        // In landscape, we don't scale at all and only crop
1124                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
1125                        mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
1126
1127                        Animation clipAnim = scaleUp
1128                                ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1129                                : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1130                        Animation translateAnim = scaleUp
1131                                ? createCurvedMotion(thumbStartX, 0,
1132                                thumbStartY - contentInsets.top, 0)
1133                                : createCurvedMotion(0, thumbStartX, 0,
1134                                        thumbStartY - contentInsets.top);
1135
1136                        set.addAnimation(clipAnim);
1137                        set.addAnimation(translateAnim);
1138                    }
1139                    a = set;
1140                    a.setZAdjustment(Animation.ZORDER_TOP);
1141                }
1142                break;
1143            }
1144            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1145                // Previous app window during the scale up
1146                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1147                    // Fade out the source activity if we are animating to a wallpaper
1148                    // activity.
1149                    a = new AlphaAnimation(1, 0);
1150                } else {
1151                    a = new AlphaAnimation(1, 1);
1152                }
1153                break;
1154            }
1155            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1156                // Target app window during the scale down
1157                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1158                    // Fade in the destination activity if we are animating from a wallpaper
1159                    // activity.
1160                    a = new AlphaAnimation(0, 1);
1161                } else {
1162                    a = new AlphaAnimation(1, 1);
1163                }
1164                break;
1165            }
1166            default:
1167                throw new RuntimeException("Invalid thumbnail transition state");
1168        }
1169
1170        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
1171                getAspectScaleDuration(), getAspectScaleInterpolator());
1172    }
1173
1174    private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
1175            @Nullable Rect surfaceInsets, int taskId) {
1176        getNextAppTransitionStartRect(taskId, mTmpRect);
1177        return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
1178                true);
1179    }
1180
1181    private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
1182            @Nullable Rect surfaceInsets, int taskId) {
1183        getNextAppTransitionStartRect(taskId, mTmpRect);
1184        return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
1185                false);
1186    }
1187
1188    private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
1189            Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
1190        final float sourceWidth = sourceFrame.width();
1191        final float sourceHeight = sourceFrame.height();
1192        final float destWidth = destFrame.width();
1193        final float destHeight = destFrame.height();
1194        final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
1195        final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
1196        AnimationSet set = new AnimationSet(true);
1197        final int surfaceInsetsH = surfaceInsets == null
1198                ? 0 : surfaceInsets.left + surfaceInsets.right;
1199        final int surfaceInsetsV = surfaceInsets == null
1200                ? 0 : surfaceInsets.top + surfaceInsets.bottom;
1201        // We want the scaling to happen from the center of the surface. In order to achieve that,
1202        // we need to account for surface insets that will be used to enlarge the surface.
1203        final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
1204        final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
1205        final ScaleAnimation scale = enter ?
1206                new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
1207                : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
1208        final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
1209        final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
1210        final int destHCenter = destFrame.left + destFrame.width() / 2;
1211        final int destVCenter = destFrame.top + destFrame.height() / 2;
1212        final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
1213        final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
1214        final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
1215                : new TranslateAnimation(0, fromX, 0, fromY);
1216        set.addAnimation(scale);
1217        set.addAnimation(translation);
1218
1219        final IRemoteCallback callback = mAnimationFinishedCallback;
1220        if (callback != null) {
1221            set.setAnimationListener(new Animation.AnimationListener() {
1222                @Override
1223                public void onAnimationStart(Animation animation) { }
1224
1225                @Override
1226                public void onAnimationEnd(Animation animation) {
1227                    mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
1228                }
1229
1230                @Override
1231                public void onAnimationRepeat(Animation animation) { }
1232            });
1233        }
1234        return set;
1235    }
1236
1237    /**
1238     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
1239     * when a thumbnail is specified with the pending animation override.
1240     */
1241    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
1242            Bitmap thumbnailHeader) {
1243        Animation a;
1244        getDefaultNextAppTransitionStartRect(mTmpRect);
1245        final int thumbWidthI = thumbnailHeader.getWidth();
1246        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1247        final int thumbHeightI = thumbnailHeader.getHeight();
1248        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1249
1250        if (mNextAppTransitionScaleUp) {
1251            // Animation for the thumbnail zooming from its initial size to the full screen
1252            float scaleW = appWidth / thumbWidth;
1253            float scaleH = appHeight / thumbHeight;
1254            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1255                    computePivot(mTmpRect.left, 1 / scaleW),
1256                    computePivot(mTmpRect.top, 1 / scaleH));
1257            scale.setInterpolator(mDecelerateInterpolator);
1258
1259            Animation alpha = new AlphaAnimation(1, 0);
1260            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
1261
1262            // This AnimationSet uses the Interpolators assigned above.
1263            AnimationSet set = new AnimationSet(false);
1264            set.addAnimation(scale);
1265            set.addAnimation(alpha);
1266            a = set;
1267        } else {
1268            // Animation for the thumbnail zooming down from the full screen to its final size
1269            float scaleW = appWidth / thumbWidth;
1270            float scaleH = appHeight / thumbHeight;
1271            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1272                    computePivot(mTmpRect.left, 1 / scaleW),
1273                    computePivot(mTmpRect.top, 1 / scaleH));
1274        }
1275
1276        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1277    }
1278
1279    /**
1280     * This animation is created when we are doing a thumbnail transition, for the activity that is
1281     * leaving, and the activity that is entering.
1282     */
1283    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
1284            int transit, int taskId) {
1285        final int appWidth = containingFrame.width();
1286        final int appHeight = containingFrame.height();
1287        Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
1288        Animation a;
1289        getDefaultNextAppTransitionStartRect(mTmpRect);
1290        final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
1291        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1292        final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
1293        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1294
1295        switch (thumbTransitState) {
1296            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
1297                // Entering app scales up with the thumbnail
1298                float scaleW = thumbWidth / appWidth;
1299                float scaleH = thumbHeight / appHeight;
1300                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1301                        computePivot(mTmpRect.left, scaleW),
1302                        computePivot(mTmpRect.top, scaleH));
1303                break;
1304            }
1305            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1306                // Exiting app while the thumbnail is scaling up should fade or stay in place
1307                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1308                    // Fade out while bringing up selected activity. This keeps the
1309                    // current activity from showing through a launching wallpaper
1310                    // activity.
1311                    a = new AlphaAnimation(1, 0);
1312                } else {
1313                    // noop animation
1314                    a = new AlphaAnimation(1, 1);
1315                }
1316                break;
1317            }
1318            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1319                // Entering the other app, it should just be visible while we scale the thumbnail
1320                // down above it
1321                a = new AlphaAnimation(1, 1);
1322                break;
1323            }
1324            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1325                // Exiting the current app, the app should scale down with the thumbnail
1326                float scaleW = thumbWidth / appWidth;
1327                float scaleH = thumbHeight / appHeight;
1328                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1329                        computePivot(mTmpRect.left, scaleW),
1330                        computePivot(mTmpRect.top, scaleH));
1331
1332                Animation alpha = new AlphaAnimation(1, 0);
1333
1334                AnimationSet set = new AnimationSet(true);
1335                set.addAnimation(scale);
1336                set.addAnimation(alpha);
1337                set.setZAdjustment(Animation.ZORDER_TOP);
1338                a = set;
1339                break;
1340            }
1341            default:
1342                throw new RuntimeException("Invalid thumbnail transition state");
1343        }
1344
1345        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1346    }
1347
1348    private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
1349        getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
1350        final int left = mTmpFromClipRect.left;
1351        final int top = mTmpFromClipRect.top;
1352        mTmpFromClipRect.offset(-left, -top);
1353        // TODO: Isn't that strange that we ignore exact position of the containingFrame?
1354        mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
1355        AnimationSet set = new AnimationSet(true);
1356        float fromWidth = mTmpFromClipRect.width();
1357        float toWidth = mTmpToClipRect.width();
1358        float fromHeight = mTmpFromClipRect.height();
1359        // While the window might span the whole display, the actual content will be cropped to the
1360        // system decoration frame, for example when the window is docked. We need to take into
1361        // account the visible height when constructing the animation.
1362        float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
1363        int translateAdjustment = 0;
1364        if (fromWidth <= toWidth && fromHeight <= toHeight) {
1365            // The final window is larger in both dimensions than current window (e.g. we are
1366            // maximizing), so we can simply unclip the new window and there will be no disappearing
1367            // frame.
1368            set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
1369        } else {
1370            // The disappearing window has one larger dimension. We need to apply scaling, so the
1371            // first frame of the entry animation matches the old window.
1372            set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
1373            // We might not be going exactly full screen, but instead be aligned under the status
1374            // bar using cropping. We still need to account for the cropped part, which will also
1375            // be scaled.
1376            translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
1377        }
1378
1379        // We animate the translation from the old position of the removed window, to the new
1380        // position of the added window. The latter might not be full screen, for example docked for
1381        // docked windows.
1382        TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
1383                0, top - containingFrame.top - translateAdjustment, 0);
1384        set.addAnimation(translate);
1385        set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
1386        set.setZAdjustment(Animation.ZORDER_TOP);
1387        return set;
1388    }
1389
1390    /**
1391     * @return true if and only if the first frame of the transition can be skipped, i.e. the first
1392     *         frame of the transition doesn't change the visuals on screen, so we can start
1393     *         directly with the second one
1394     */
1395    boolean canSkipFirstFrame() {
1396        return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
1397                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
1398                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1399    }
1400
1401    /**
1402     *
1403     * @param frame These are the bounds of the window when it finishes the animation. This is where
1404     *              the animation must usually finish in entrance animation, as the next frame will
1405     *              display the window at these coordinates. In case of exit animation, this is
1406     *              where the animation must start, as the frame before the animation is displaying
1407     *              the window at these bounds.
1408     * @param insets Knowing where the window will be positioned is not enough. Some parts of the
1409     *               window might be obscured, usually by the system windows (status bar and
1410     *               navigation bar) and we use content insets to convey that information. This
1411     *               usually affects the animation aspects vertically, as the system decoration is
1412     *               at the top and the bottom. For example when we animate from full screen to
1413     *               recents, we want to exclude the covered parts, because they won't match the
1414     *               thumbnail after the last frame is executed.
1415     * @param surfaceInsets In rare situation the surface is larger than the content and we need to
1416     *                      know about this to make the animation frames match. We currently use
1417     *                      this for freeform windows, which have larger surfaces to display
1418     *                      shadows. When we animate them from recents, we want to match the content
1419     *                      to the recents thumbnail and hence need to account for the surface being
1420     *                      bigger.
1421     */
1422    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
1423            int orientation, Rect frame, Rect displayFrame, Rect insets,
1424            @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
1425            int taskId) {
1426        Animation a;
1427        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
1428                || transit == TRANSIT_TASK_OPEN
1429                || transit == TRANSIT_TASK_TO_FRONT)) {
1430            a = loadAnimationRes(lp, enter
1431                    ? com.android.internal.R.anim.voice_activity_open_enter
1432                    : com.android.internal.R.anim.voice_activity_open_exit);
1433            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1434                    "applyAnimation voice:"
1435                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1436                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1437        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
1438                || transit == TRANSIT_TASK_CLOSE
1439                || transit == TRANSIT_TASK_TO_BACK)) {
1440            a = loadAnimationRes(lp, enter
1441                    ? com.android.internal.R.anim.voice_activity_close_enter
1442                    : com.android.internal.R.anim.voice_activity_close_exit);
1443            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1444                    "applyAnimation voice:"
1445                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1446                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1447        } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
1448            a = createRelaunchAnimation(frame, insets);
1449            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1450                    "applyAnimation:"
1451                    + " anim=" + a + " nextAppTransition=" + mNextAppTransition
1452                    + " transit=" + appTransitionToString(transit)
1453                    + " Callers=" + Debug.getCallers(3));
1454        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
1455            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
1456                    mNextAppTransitionEnter : mNextAppTransitionExit);
1457            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1458                    "applyAnimation:"
1459                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
1460                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1461                    + " Callers=" + Debug.getCallers(3));
1462        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
1463            a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
1464            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1465                    "applyAnimation:"
1466                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
1467                    + " transit=" + appTransitionToString(transit)
1468                    + " Callers=" + Debug.getCallers(3));
1469        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
1470            a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
1471            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1472                    "applyAnimation:"
1473                            + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
1474                            + " transit=" + appTransitionToString(transit)
1475                            + " Callers=" + Debug.getCallers(3));
1476        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
1477            a = createScaleUpAnimationLocked(transit, enter, frame);
1478            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1479                    "applyAnimation:"
1480                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
1481                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1482                    + " Callers=" + Debug.getCallers(3));
1483        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
1484                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
1485            mNextAppTransitionScaleUp =
1486                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
1487            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
1488                    frame, transit, taskId);
1489            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1490                String animName = mNextAppTransitionScaleUp ?
1491                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
1492                Slog.v(TAG, "applyAnimation:"
1493                        + " anim=" + a + " nextAppTransition=" + animName
1494                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1495                        + " Callers=" + Debug.getCallers(3));
1496            }
1497        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
1498                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
1499            mNextAppTransitionScaleUp =
1500                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
1501            a = createAspectScaledThumbnailEnterExitAnimationLocked(
1502                    getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
1503                    insets, surfaceInsets, freeform, taskId);
1504            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1505                String animName = mNextAppTransitionScaleUp ?
1506                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
1507                Slog.v(TAG, "applyAnimation:"
1508                        + " anim=" + a + " nextAppTransition=" + animName
1509                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1510                        + " Callers=" + Debug.getCallers(3));
1511            }
1512        } else {
1513            int animAttr = 0;
1514            switch (transit) {
1515                case TRANSIT_ACTIVITY_OPEN:
1516                    animAttr = enter
1517                            ? WindowAnimation_activityOpenEnterAnimation
1518                            : WindowAnimation_activityOpenExitAnimation;
1519                    break;
1520                case TRANSIT_ACTIVITY_CLOSE:
1521                    animAttr = enter
1522                            ? WindowAnimation_activityCloseEnterAnimation
1523                            : WindowAnimation_activityCloseExitAnimation;
1524                    break;
1525                case TRANSIT_DOCK_TASK_FROM_RECENTS:
1526                case TRANSIT_TASK_OPEN:
1527                    animAttr = enter
1528                            ? WindowAnimation_taskOpenEnterAnimation
1529                            : WindowAnimation_taskOpenExitAnimation;
1530                    break;
1531                case TRANSIT_TASK_CLOSE:
1532                    animAttr = enter
1533                            ? WindowAnimation_taskCloseEnterAnimation
1534                            : WindowAnimation_taskCloseExitAnimation;
1535                    break;
1536                case TRANSIT_TASK_TO_FRONT:
1537                    animAttr = enter
1538                            ? WindowAnimation_taskToFrontEnterAnimation
1539                            : WindowAnimation_taskToFrontExitAnimation;
1540                    break;
1541                case TRANSIT_TASK_TO_BACK:
1542                    animAttr = enter
1543                            ? WindowAnimation_taskToBackEnterAnimation
1544                            : WindowAnimation_taskToBackExitAnimation;
1545                    break;
1546                case TRANSIT_WALLPAPER_OPEN:
1547                    animAttr = enter
1548                            ? WindowAnimation_wallpaperOpenEnterAnimation
1549                            : WindowAnimation_wallpaperOpenExitAnimation;
1550                    break;
1551                case TRANSIT_WALLPAPER_CLOSE:
1552                    animAttr = enter
1553                            ? WindowAnimation_wallpaperCloseEnterAnimation
1554                            : WindowAnimation_wallpaperCloseExitAnimation;
1555                    break;
1556                case TRANSIT_WALLPAPER_INTRA_OPEN:
1557                    animAttr = enter
1558                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
1559                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
1560                    break;
1561                case TRANSIT_WALLPAPER_INTRA_CLOSE:
1562                    animAttr = enter
1563                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
1564                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
1565                    break;
1566                case TRANSIT_TASK_OPEN_BEHIND:
1567                    animAttr = enter
1568                            ? WindowAnimation_launchTaskBehindSourceAnimation
1569                            : WindowAnimation_launchTaskBehindTargetAnimation;
1570            }
1571            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
1572            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1573                    "applyAnimation:"
1574                    + " anim=" + a
1575                    + " animAttr=0x" + Integer.toHexString(animAttr)
1576                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1577                    + " Callers=" + Debug.getCallers(3));
1578        }
1579        return a;
1580    }
1581
1582    int getAppStackClipMode() {
1583        return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
1584                || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
1585                || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
1586                ? STACK_CLIP_NONE
1587                : STACK_CLIP_AFTER_ANIM;
1588    }
1589
1590    void postAnimationCallback() {
1591        if (mNextAppTransitionCallback != null) {
1592            mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
1593                    mNextAppTransitionCallback));
1594            mNextAppTransitionCallback = null;
1595        }
1596    }
1597
1598    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
1599            IRemoteCallback startedCallback) {
1600        if (isTransitionSet()) {
1601            clear();
1602            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
1603            mNextAppTransitionPackage = packageName;
1604            mNextAppTransitionEnter = enterAnim;
1605            mNextAppTransitionExit = exitAnim;
1606            postAnimationCallback();
1607            mNextAppTransitionCallback = startedCallback;
1608        } else {
1609            postAnimationCallback();
1610        }
1611    }
1612
1613    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
1614            int startHeight) {
1615        if (isTransitionSet()) {
1616            clear();
1617            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
1618            putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
1619                    startY + startHeight, null);
1620            postAnimationCallback();
1621        }
1622    }
1623
1624    void overridePendingAppTransitionClipReveal(int startX, int startY,
1625                                                int startWidth, int startHeight) {
1626        if (isTransitionSet()) {
1627            clear();
1628            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1629            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
1630            postAnimationCallback();
1631        }
1632    }
1633
1634    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
1635                                           IRemoteCallback startedCallback, boolean scaleUp) {
1636        if (isTransitionSet()) {
1637            clear();
1638            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
1639                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
1640            mNextAppTransitionScaleUp = scaleUp;
1641            putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
1642            postAnimationCallback();
1643            mNextAppTransitionCallback = startedCallback;
1644        } else {
1645            postAnimationCallback();
1646        }
1647    }
1648
1649    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
1650            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
1651        if (isTransitionSet()) {
1652            clear();
1653            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1654                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1655            mNextAppTransitionScaleUp = scaleUp;
1656            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
1657                    srcThumb);
1658            postAnimationCallback();
1659            mNextAppTransitionCallback = startedCallback;
1660        } else {
1661            postAnimationCallback();
1662        }
1663    }
1664
1665    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
1666            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
1667            boolean scaleUp) {
1668        if (isTransitionSet()) {
1669            clear();
1670            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1671                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1672            mNextAppTransitionScaleUp = scaleUp;
1673            if (specs != null) {
1674                for (int i = 0; i < specs.length; i++) {
1675                    AppTransitionAnimationSpec spec = specs[i];
1676                    if (spec != null) {
1677                        mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
1678                        if (i == 0) {
1679                            // In full screen mode, the transition code depends on the default spec
1680                            // to be set.
1681                            Rect rect = spec.rect;
1682                            putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
1683                                    rect.width(), rect.height(), spec.bitmap);
1684                        }
1685                    }
1686                }
1687            }
1688            postAnimationCallback();
1689            mNextAppTransitionCallback = onAnimationStartedCallback;
1690            mAnimationFinishedCallback = onAnimationFinishedCallback;
1691        } else {
1692            postAnimationCallback();
1693        }
1694    }
1695
1696    void overridePendingAppTransitionMultiThumbFuture(
1697            IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
1698            boolean scaleUp) {
1699        if (isTransitionSet()) {
1700            clear();
1701            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1702                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1703            mNextAppTransitionAnimationsSpecsFuture = specsFuture;
1704            mNextAppTransitionScaleUp = scaleUp;
1705            mNextAppTransitionFutureCallback = callback;
1706        }
1707    }
1708
1709    void overrideInPlaceAppTransition(String packageName, int anim) {
1710        if (isTransitionSet()) {
1711            clear();
1712            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
1713            mNextAppTransitionPackage = packageName;
1714            mNextAppTransitionInPlace = anim;
1715        } else {
1716            postAnimationCallback();
1717        }
1718    }
1719
1720    /**
1721     * If a future is set for the app transition specs, fetch it in another thread.
1722     */
1723    private void fetchAppTransitionSpecsFromFuture() {
1724        if (mNextAppTransitionAnimationsSpecsFuture != null) {
1725            mNextAppTransitionAnimationsSpecsPending = true;
1726            final IAppTransitionAnimationSpecsFuture future
1727                    = mNextAppTransitionAnimationsSpecsFuture;
1728            mNextAppTransitionAnimationsSpecsFuture = null;
1729            mDefaultExecutor.execute(new Runnable() {
1730                @Override
1731                public void run() {
1732                    AppTransitionAnimationSpec[] specs = null;
1733                    try {
1734                        specs = future.get();
1735                    } catch (RemoteException e) {
1736                        Slog.w(TAG, "Failed to fetch app transition specs: " + e);
1737                    }
1738                    synchronized (mService.mWindowMap) {
1739                        mNextAppTransitionAnimationsSpecsPending = false;
1740                        overridePendingAppTransitionMultiThumb(specs,
1741                                mNextAppTransitionFutureCallback, null /* finishedCallback */,
1742                                mNextAppTransitionScaleUp);
1743                        mNextAppTransitionFutureCallback = null;
1744                        if (specs != null) {
1745                            mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
1746                        }
1747                    }
1748                    mService.requestTraversal();
1749                }
1750            });
1751        }
1752    }
1753
1754    @Override
1755    public String toString() {
1756        return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
1757    }
1758
1759    /**
1760     * Returns the human readable name of a window transition.
1761     *
1762     * @param transition The window transition.
1763     * @return The transition symbolic name.
1764     */
1765    public static String appTransitionToString(int transition) {
1766        switch (transition) {
1767            case TRANSIT_UNSET: {
1768                return "TRANSIT_UNSET";
1769            }
1770            case TRANSIT_NONE: {
1771                return "TRANSIT_NONE";
1772            }
1773            case TRANSIT_ACTIVITY_OPEN: {
1774                return "TRANSIT_ACTIVITY_OPEN";
1775            }
1776            case TRANSIT_ACTIVITY_CLOSE: {
1777                return "TRANSIT_ACTIVITY_CLOSE";
1778            }
1779            case TRANSIT_TASK_OPEN: {
1780                return "TRANSIT_TASK_OPEN";
1781            }
1782            case TRANSIT_TASK_CLOSE: {
1783                return "TRANSIT_TASK_CLOSE";
1784            }
1785            case TRANSIT_TASK_TO_FRONT: {
1786                return "TRANSIT_TASK_TO_FRONT";
1787            }
1788            case TRANSIT_TASK_TO_BACK: {
1789                return "TRANSIT_TASK_TO_BACK";
1790            }
1791            case TRANSIT_WALLPAPER_CLOSE: {
1792                return "TRANSIT_WALLPAPER_CLOSE";
1793            }
1794            case TRANSIT_WALLPAPER_OPEN: {
1795                return "TRANSIT_WALLPAPER_OPEN";
1796            }
1797            case TRANSIT_WALLPAPER_INTRA_OPEN: {
1798                return "TRANSIT_WALLPAPER_INTRA_OPEN";
1799            }
1800            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1801                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1802            }
1803            case TRANSIT_TASK_OPEN_BEHIND: {
1804                return "TRANSIT_TASK_OPEN_BEHIND";
1805            }
1806            case TRANSIT_ACTIVITY_RELAUNCH: {
1807                return "TRANSIT_ACTIVITY_RELAUNCH";
1808            }
1809            case TRANSIT_DOCK_TASK_FROM_RECENTS: {
1810                return "TRANSIT_DOCK_TASK_FROM_RECENTS";
1811            }
1812            default: {
1813                return "<UNKNOWN>";
1814            }
1815        }
1816    }
1817
1818    private String appStateToString() {
1819        switch (mAppTransitionState) {
1820            case APP_STATE_IDLE:
1821                return "APP_STATE_IDLE";
1822            case APP_STATE_READY:
1823                return "APP_STATE_READY";
1824            case APP_STATE_RUNNING:
1825                return "APP_STATE_RUNNING";
1826            case APP_STATE_TIMEOUT:
1827                return "APP_STATE_TIMEOUT";
1828            default:
1829                return "unknown state=" + mAppTransitionState;
1830        }
1831    }
1832
1833    private String transitTypeToString() {
1834        switch (mNextAppTransitionType) {
1835            case NEXT_TRANSIT_TYPE_NONE:
1836                return "NEXT_TRANSIT_TYPE_NONE";
1837            case NEXT_TRANSIT_TYPE_CUSTOM:
1838                return "NEXT_TRANSIT_TYPE_CUSTOM";
1839            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1840                return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE";
1841            case NEXT_TRANSIT_TYPE_SCALE_UP:
1842                return "NEXT_TRANSIT_TYPE_SCALE_UP";
1843            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1844                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1845            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1846                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1847            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1848                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1849            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1850                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1851            default:
1852                return "unknown type=" + mNextAppTransitionType;
1853        }
1854    }
1855
1856    @Override
1857    public void dump(PrintWriter pw, String prefix) {
1858        pw.print(prefix); pw.println(this);
1859        pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString());
1860        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1861            pw.print(prefix); pw.print("mNextAppTransitionType=");
1862                    pw.println(transitTypeToString());
1863        }
1864        switch (mNextAppTransitionType) {
1865            case NEXT_TRANSIT_TYPE_CUSTOM:
1866                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1867                        pw.println(mNextAppTransitionPackage);
1868                pw.print(prefix); pw.print("mNextAppTransitionEnter=0x");
1869                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
1870                        pw.print(" mNextAppTransitionExit=0x");
1871                        pw.println(Integer.toHexString(mNextAppTransitionExit));
1872                break;
1873            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1874                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1875                        pw.println(mNextAppTransitionPackage);
1876                pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
1877                        pw.print(Integer.toHexString(mNextAppTransitionInPlace));
1878                break;
1879            case NEXT_TRANSIT_TYPE_SCALE_UP: {
1880                getDefaultNextAppTransitionStartRect(mTmpRect);
1881                pw.print(prefix); pw.print("mNextAppTransitionStartX=");
1882                        pw.print(mTmpRect.left);
1883                        pw.print(" mNextAppTransitionStartY=");
1884                        pw.println(mTmpRect.top);
1885                pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
1886                        pw.print(mTmpRect.width());
1887                        pw.print(" mNextAppTransitionStartHeight=");
1888                        pw.println(mTmpRect.height());
1889                break;
1890            }
1891            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1892            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1893            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1894            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
1895                pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
1896                        pw.println(mDefaultNextAppTransitionAnimationSpec);
1897                pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
1898                        pw.println(mNextAppTransitionAnimationsSpecs);
1899                pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
1900                        pw.println(mNextAppTransitionScaleUp);
1901                break;
1902            }
1903        }
1904        if (mNextAppTransitionCallback != null) {
1905            pw.print(prefix); pw.print("mNextAppTransitionCallback=");
1906                    pw.println(mNextAppTransitionCallback);
1907        }
1908    }
1909
1910    public void setCurrentUser(int newUserId) {
1911        mCurrentUserId = newUserId;
1912    }
1913
1914    /**
1915     * @return true if transition is not running and should not be skipped, false if transition is
1916     *         already running
1917     */
1918    boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
1919        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
1920                + " transit=" + appTransitionToString(transit)
1921                + " " + this
1922                + " alwaysKeepCurrent=" + alwaysKeepCurrent
1923                + " Callers=" + Debug.getCallers(3));
1924        if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
1925            setAppTransition(transit);
1926        } else if (!alwaysKeepCurrent) {
1927            if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
1928                // Opening a new task always supersedes a close for the anim.
1929                setAppTransition(transit);
1930            } else if (transit == TRANSIT_ACTIVITY_OPEN
1931                    && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
1932                // Opening a new activity always supersedes a close for the anim.
1933                setAppTransition(transit);
1934            }
1935        }
1936        boolean prepared = prepare();
1937        if (isTransitionSet()) {
1938            mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
1939            mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
1940        }
1941        return prepared;
1942    }
1943
1944    /**
1945     * @return whether the specified {@param uiMode} is the TV mode.
1946     */
1947    private boolean isTvUiMode(int uiMode) {
1948        return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0;
1949    }
1950}
1951