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