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