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