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