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