AppTransition.java revision 8448f339f9207aa1e554b1a1e537ce269462807a
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 int appWidth = appRect.width();
881
882        float scaleW = appWidth / thumbWidth;
883        getNextAppTransitionStartRect(taskId, mTmpRect);
884        final float toY = mTmpRect.height() / 2 * (scaleW - 1f) + appRect.top;
885        final float fromY = mTmpRect.top;
886        final float toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
887        final float fromX = mTmpRect.left;
888        final float pivotX = mTmpRect.width() / 2;
889        final float pivotY = mTmpRect.height() / 2;
890        if (mNextAppTransitionScaleUp) {
891            // Animation up from the thumbnail to the full screen
892            Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
893            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
894            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
895            Animation alpha = new AlphaAnimation(1f, 0f);
896            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
897            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
898            Animation translate = new TranslateAnimation(fromX, toX, fromY, toY);
899            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
900            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
901
902            mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
903            mTmpToClipRect.set(appRect);
904
905            // Containing frame is in screen space, but we need the clip rect in the
906            // app space.
907            mTmpToClipRect.offsetTo(0, 0);
908            mTmpToClipRect.right = (int) (mTmpToClipRect.right * scaleW);
909            mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom * scaleW);
910
911            if (contentInsets != null) {
912                mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
913                        (int) (-contentInsets.top * scaleW),
914                        (int) (-contentInsets.right * scaleW),
915                        (int) (-contentInsets.bottom * scaleW));
916            }
917
918            Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
919            clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
920            clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
921
922            // This AnimationSet uses the Interpolators assigned above.
923            AnimationSet set = new AnimationSet(false);
924            set.addAnimation(scale);
925            set.addAnimation(alpha);
926            set.addAnimation(translate);
927            set.addAnimation(clipAnim);
928            a = set;
929        } else {
930            // Animation down from the full screen to the thumbnail
931            Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
932            scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
933            scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
934            Animation alpha = new AlphaAnimation(0f, 1f);
935            alpha.setInterpolator(mThumbnailFadeInInterpolator);
936            alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
937            Animation translate = new TranslateAnimation(toX, fromX, toY, fromY);
938            translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
939            translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
940
941            // This AnimationSet uses the Interpolators assigned above.
942            AnimationSet set = new AnimationSet(false);
943            set.addAnimation(scale);
944            set.addAnimation(alpha);
945            set.addAnimation(translate);
946            a = set;
947
948        }
949        return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
950                TOUCH_RESPONSE_INTERPOLATOR);
951    }
952
953    /**
954     * This alternate animation is created when we are doing a thumbnail transition, for the
955     * activity that is leaving, and the activity that is entering.
956     */
957    Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
958            int orientation, int transit, Rect containingFrame, Rect contentInsets,
959            @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
960        Animation a;
961        final int appWidth = containingFrame.width();
962        final int appHeight = containingFrame.height();
963        getDefaultNextAppTransitionStartRect(mTmpRect);
964        final int thumbWidthI = mTmpRect.width();
965        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
966        final int thumbHeightI = mTmpRect.height();
967        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
968        final int thumbStartX = mTmpRect.left - containingFrame.left;
969        final int thumbStartY = mTmpRect.top - containingFrame.top;
970
971        // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
972        float scale = 1f;
973        int scaledTopDecor = 0;
974
975        switch (thumbTransitState) {
976            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
977            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
978                final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
979                if (freeform && scaleUp) {
980                    a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
981                            containingFrame, surfaceInsets, taskId);
982                } else if (freeform) {
983                    a = createAspectScaledThumbnailExitFreeformAnimationLocked(
984                            containingFrame, surfaceInsets, taskId);
985                } else {
986                    AnimationSet set = new AnimationSet(true);
987
988                    // In portrait, we scale to fit the width
989                    mTmpFromClipRect.set(containingFrame);
990                    mTmpToClipRect.set(containingFrame);
991
992                    // Containing frame is in screen space, but we need the clip rect in the
993                    // app space.
994                    mTmpFromClipRect.offsetTo(0, 0);
995                    mTmpToClipRect.offsetTo(0, 0);
996
997                    // Exclude insets region from the source clip.
998                    mTmpFromClipRect.inset(contentInsets);
999                    mNextAppTransitionInsets.set(contentInsets);
1000
1001                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
1002                        // We scale the width and clip to the top/left square
1003                        Animation scaleAnim = new ScaleAnimation(
1004                                scaleUp ? scale : 1, scaleUp ? 1 : scale,
1005                                scaleUp ? scale : 1, scaleUp ? 1 : scale,
1006                                containingFrame.width() / 2,
1007                                containingFrame.height() / 2 + contentInsets.top);
1008                        final float targetX = (mTmpRect.left - containingFrame.left);
1009                        final float x = containingFrame.width() / 2
1010                                - containingFrame.width() / 2 * scale;
1011                        final float targetY = (mTmpRect.top - containingFrame.top);
1012                        final float y = containingFrame.height() / 2
1013                                - containingFrame.height() / 2 * scale;
1014                        final float startX = targetX - x;
1015                        final float startY = targetY - y;
1016                        Animation clipAnim = scaleUp
1017                                ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1018                                : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1019                        Animation translateAnim = scaleUp
1020                                ? new TranslateAnimation(startX, 0, startY - scaledTopDecor, 0)
1021                                : new TranslateAnimation(0, startX, 0, startY - scaledTopDecor);
1022                        set.addAnimation(clipAnim);
1023                        set.addAnimation(scaleAnim);
1024                        set.addAnimation(translateAnim);
1025
1026                    } else {
1027                        // In landscape, we don't scale at all and only crop
1028                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
1029                        mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
1030
1031                        Animation clipAnim = scaleUp
1032                                ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
1033                                : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
1034                        Animation translateAnim = scaleUp
1035                                ? new TranslateAnimation(thumbStartX, 0, thumbStartY - contentInsets.top, 0)
1036                                : new TranslateAnimation(0, thumbStartX, 0, thumbStartY - contentInsets.top);
1037
1038                        set.addAnimation(clipAnim);
1039                        set.addAnimation(translateAnim);
1040                    }
1041                    set.setZAdjustment(Animation.ZORDER_TOP);
1042                    a = set;
1043                    a.setZAdjustment(Animation.ZORDER_TOP);
1044                }
1045                break;
1046            }
1047            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1048                // Previous app window during the scale up
1049                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1050                    // Fade out the source activity if we are animating to a wallpaper
1051                    // activity.
1052                    a = new AlphaAnimation(1, 0);
1053                } else {
1054                    a = new AlphaAnimation(1, 1);
1055                }
1056                break;
1057            }
1058            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1059                // Target app window during the scale down
1060                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1061                    // Fade in the destination activity if we are animating from a wallpaper
1062                    // activity.
1063                    a = new AlphaAnimation(0, 1);
1064                } else {
1065                    a = new AlphaAnimation(1, 1);
1066                }
1067                break;
1068            }
1069            default:
1070                throw new RuntimeException("Invalid thumbnail transition state");
1071        }
1072
1073        int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
1074                THUMBNAIL_APP_TRANSITION_DURATION);
1075        return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
1076                TOUCH_RESPONSE_INTERPOLATOR);
1077    }
1078
1079    private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
1080            @Nullable Rect surfaceInsets, int taskId) {
1081        getNextAppTransitionStartRect(taskId, mTmpRect);
1082        return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets,
1083                true);
1084    }
1085
1086    private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame,
1087            @Nullable Rect surfaceInsets, int taskId) {
1088        getNextAppTransitionStartRect(taskId, mTmpRect);
1089        return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets,
1090                false);
1091    }
1092
1093    private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame,
1094            Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) {
1095        final float sourceWidth = sourceFrame.width();
1096        final float sourceHeight = sourceFrame.height();
1097        final float destWidth = destFrame.width();
1098        final float destHeight = destFrame.height();
1099        final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth;
1100        final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight;
1101        AnimationSet set = new AnimationSet(true);
1102        final int surfaceInsetsH = surfaceInsets == null
1103                ? 0 : surfaceInsets.left + surfaceInsets.right;
1104        final int surfaceInsetsV = surfaceInsets == null
1105                ? 0 : surfaceInsets.top + surfaceInsets.bottom;
1106        // We want the scaling to happen from the center of the surface. In order to achieve that,
1107        // we need to account for surface insets that will be used to enlarge the surface.
1108        final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2;
1109        final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2;
1110        final ScaleAnimation scale = enter ?
1111                new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter)
1112                : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter);
1113        final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2;
1114        final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2;
1115        final int destHCenter = destFrame.left + destFrame.width() / 2;
1116        final int destVCenter = destFrame.top + destFrame.height() / 2;
1117        final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter;
1118        final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter;
1119        final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0)
1120                : new TranslateAnimation(0, fromX, 0, fromY);
1121        set.addAnimation(scale);
1122        set.addAnimation(translation);
1123
1124        final IRemoteCallback callback = mAnimationFinishedCallback;
1125        if (callback != null) {
1126            set.setAnimationListener(new Animation.AnimationListener() {
1127                @Override
1128                public void onAnimationStart(Animation animation) { }
1129
1130                @Override
1131                public void onAnimationEnd(Animation animation) {
1132                    mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
1133                }
1134
1135                @Override
1136                public void onAnimationRepeat(Animation animation) { }
1137            });
1138        }
1139        return set;
1140    }
1141
1142    /**
1143     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
1144     * when a thumbnail is specified with the pending animation override.
1145     */
1146    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit,
1147            Bitmap thumbnailHeader) {
1148        Animation a;
1149        getDefaultNextAppTransitionStartRect(mTmpRect);
1150        final int thumbWidthI = thumbnailHeader.getWidth();
1151        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1152        final int thumbHeightI = thumbnailHeader.getHeight();
1153        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1154
1155        if (mNextAppTransitionScaleUp) {
1156            // Animation for the thumbnail zooming from its initial size to the full screen
1157            float scaleW = appWidth / thumbWidth;
1158            float scaleH = appHeight / thumbHeight;
1159            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1160                    computePivot(mTmpRect.left, 1 / scaleW),
1161                    computePivot(mTmpRect.top, 1 / scaleH));
1162            scale.setInterpolator(mDecelerateInterpolator);
1163
1164            Animation alpha = new AlphaAnimation(1, 0);
1165            alpha.setInterpolator(mThumbnailFadeOutInterpolator);
1166
1167            // This AnimationSet uses the Interpolators assigned above.
1168            AnimationSet set = new AnimationSet(false);
1169            set.addAnimation(scale);
1170            set.addAnimation(alpha);
1171            a = set;
1172        } else {
1173            // Animation for the thumbnail zooming down from the full screen to its final size
1174            float scaleW = appWidth / thumbWidth;
1175            float scaleH = appHeight / thumbHeight;
1176            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1177                    computePivot(mTmpRect.left, 1 / scaleW),
1178                    computePivot(mTmpRect.top, 1 / scaleH));
1179        }
1180
1181        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1182    }
1183
1184    /**
1185     * This animation is created when we are doing a thumbnail transition, for the activity that is
1186     * leaving, and the activity that is entering.
1187     */
1188    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
1189            int transit, int taskId) {
1190        final int appWidth = containingFrame.width();
1191        final int appHeight = containingFrame.height();
1192        Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
1193        Animation a;
1194        getDefaultNextAppTransitionStartRect(mTmpRect);
1195        final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth;
1196        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
1197        final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
1198        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
1199
1200        switch (thumbTransitState) {
1201            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
1202                // Entering app scales up with the thumbnail
1203                float scaleW = thumbWidth / appWidth;
1204                float scaleH = thumbHeight / appHeight;
1205                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
1206                        computePivot(mTmpRect.left, scaleW),
1207                        computePivot(mTmpRect.top, scaleH));
1208                break;
1209            }
1210            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
1211                // Exiting app while the thumbnail is scaling up should fade or stay in place
1212                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
1213                    // Fade out while bringing up selected activity. This keeps the
1214                    // current activity from showing through a launching wallpaper
1215                    // activity.
1216                    a = new AlphaAnimation(1, 0);
1217                } else {
1218                    // noop animation
1219                    a = new AlphaAnimation(1, 1);
1220                }
1221                break;
1222            }
1223            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
1224                // Entering the other app, it should just be visible while we scale the thumbnail
1225                // down above it
1226                a = new AlphaAnimation(1, 1);
1227                break;
1228            }
1229            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
1230                // Exiting the current app, the app should scale down with the thumbnail
1231                float scaleW = thumbWidth / appWidth;
1232                float scaleH = thumbHeight / appHeight;
1233                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
1234                        computePivot(mTmpRect.left, scaleW),
1235                        computePivot(mTmpRect.top, scaleH));
1236
1237                Animation alpha = new AlphaAnimation(1, 0);
1238
1239                AnimationSet set = new AnimationSet(true);
1240                set.addAnimation(scale);
1241                set.addAnimation(alpha);
1242                set.setZAdjustment(Animation.ZORDER_TOP);
1243                a = set;
1244                break;
1245            }
1246            default:
1247                throw new RuntimeException("Invalid thumbnail transition state");
1248        }
1249
1250        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
1251    }
1252
1253    private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
1254        getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
1255        final int left = mTmpFromClipRect.left;
1256        final int top = mTmpFromClipRect.top;
1257        mTmpFromClipRect.offset(-left, -top);
1258        // TODO: Isn't that strange that we ignore exact position of the containingFrame?
1259        mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
1260        AnimationSet set = new AnimationSet(true);
1261        float fromWidth = mTmpFromClipRect.width();
1262        float toWidth = mTmpToClipRect.width();
1263        float fromHeight = mTmpFromClipRect.height();
1264        // While the window might span the whole display, the actual content will be cropped to the
1265        // system decoration frame, for example when the window is docked. We need to take into
1266        // account the visible height when constructing the animation.
1267        float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
1268        int translateAdjustment = 0;
1269        if (fromWidth <= toWidth && fromHeight <= toHeight) {
1270            // The final window is larger in both dimensions than current window (e.g. we are
1271            // maximizing), so we can simply unclip the new window and there will be no disappearing
1272            // frame.
1273            set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect));
1274        } else {
1275            // The disappearing window has one larger dimension. We need to apply scaling, so the
1276            // first frame of the entry animation matches the old window.
1277            set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
1278            // We might not be going exactly full screen, but instead be aligned under the status
1279            // bar using cropping. We still need to account for the cropped part, which will also
1280            // be scaled.
1281            translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
1282        }
1283
1284        // We animate the translation from the old position of the removed window, to the new
1285        // position of the added window. The latter might not be full screen, for example docked for
1286        // docked windows.
1287        TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
1288                0, top - containingFrame.top - translateAdjustment, 0);
1289        set.addAnimation(translate);
1290        set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
1291        set.setZAdjustment(Animation.ZORDER_TOP);
1292        return set;
1293    }
1294
1295    /**
1296     * @return true if and only if the first frame of the transition can be skipped, i.e. the first
1297     *         frame of the transition doesn't change the visuals on screen, so we can start
1298     *         directly with the second one
1299     */
1300    boolean canSkipFirstFrame() {
1301        return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM
1302                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE
1303                && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1304    }
1305
1306    /**
1307     *
1308     * @param frame These are the bounds of the window when it finishes the animation. This is where
1309     *              the animation must usually finish in entrance animation, as the next frame will
1310     *              display the window at these coordinates. In case of exit animation, this is
1311     *              where the animation must start, as the frame before the animation is displaying
1312     *              the window at these bounds.
1313     * @param insets Knowing where the window will be positioned is not enough. Some parts of the
1314     *               window might be obscured, usually by the system windows (status bar and
1315     *               navigation bar) and we use content insets to convey that information. This
1316     *               usually affects the animation aspects vertically, as the system decoration is
1317     *               at the top and the bottom. For example when we animate from full screen to
1318     *               recents, we want to exclude the covered parts, because they won't match the
1319     *               thumbnail after the last frame is executed.
1320     * @param surfaceInsets In rare situation the surface is larger than the content and we need to
1321     *                      know about this to make the animation frames match. We currently use
1322     *                      this for freeform windows, which have larger surfaces to display
1323     *                      shadows. When we animate them from recents, we want to match the content
1324     *                      to the recents thumbnail and hence need to account for the surface being
1325     *                      bigger.
1326     */
1327    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
1328            int orientation, Rect frame, Rect displayFrame, Rect insets,
1329            @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
1330            int taskId) {
1331        Animation a;
1332        if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
1333                || transit == TRANSIT_TASK_OPEN
1334                || transit == TRANSIT_TASK_TO_FRONT)) {
1335            a = loadAnimationRes(lp, enter
1336                    ? com.android.internal.R.anim.voice_activity_open_enter
1337                    : com.android.internal.R.anim.voice_activity_open_exit);
1338            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1339                    "applyAnimation voice:"
1340                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1341                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1342        } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE
1343                || transit == TRANSIT_TASK_CLOSE
1344                || transit == TRANSIT_TASK_TO_BACK)) {
1345            a = loadAnimationRes(lp, enter
1346                    ? com.android.internal.R.anim.voice_activity_close_enter
1347                    : com.android.internal.R.anim.voice_activity_close_exit);
1348            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1349                    "applyAnimation voice:"
1350                    + " anim=" + a + " transit=" + appTransitionToString(transit)
1351                    + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
1352        } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
1353            a = createRelaunchAnimation(frame, insets);
1354            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1355                    "applyAnimation:"
1356                    + " anim=" + a + " nextAppTransition=" + mNextAppTransition
1357                    + " transit=" + appTransitionToString(transit)
1358                    + " Callers=" + Debug.getCallers(3));
1359        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
1360            a = loadAnimationRes(mNextAppTransitionPackage, enter ?
1361                    mNextAppTransitionEnter : mNextAppTransitionExit);
1362            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1363                    "applyAnimation:"
1364                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM"
1365                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1366                    + " Callers=" + Debug.getCallers(3));
1367        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
1368            a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace);
1369            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1370                    "applyAnimation:"
1371                    + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE"
1372                    + " transit=" + appTransitionToString(transit)
1373                    + " Callers=" + Debug.getCallers(3));
1374        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
1375            a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame);
1376            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1377                    "applyAnimation:"
1378                            + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
1379                            + " transit=" + appTransitionToString(transit)
1380                            + " Callers=" + Debug.getCallers(3));
1381        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
1382            a = createScaleUpAnimationLocked(transit, enter, frame);
1383            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1384                    "applyAnimation:"
1385                    + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
1386                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1387                    + " Callers=" + Debug.getCallers(3));
1388        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
1389                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
1390            mNextAppTransitionScaleUp =
1391                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
1392            a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
1393                    frame, transit, taskId);
1394            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1395                String animName = mNextAppTransitionScaleUp ?
1396                        "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
1397                Slog.v(TAG, "applyAnimation:"
1398                        + " anim=" + a + " nextAppTransition=" + animName
1399                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1400                        + " Callers=" + Debug.getCallers(3));
1401            }
1402        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
1403                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
1404            mNextAppTransitionScaleUp =
1405                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
1406            a = createAspectScaledThumbnailEnterExitAnimationLocked(
1407                    getThumbnailTransitionState(enter), orientation, transit, frame,
1408                    insets, surfaceInsets, freeform, taskId);
1409            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
1410                String animName = mNextAppTransitionScaleUp ?
1411                        "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
1412                Slog.v(TAG, "applyAnimation:"
1413                        + " anim=" + a + " nextAppTransition=" + animName
1414                        + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1415                        + " Callers=" + Debug.getCallers(3));
1416            }
1417        } else {
1418            int animAttr = 0;
1419            switch (transit) {
1420                case TRANSIT_ACTIVITY_OPEN:
1421                    animAttr = enter
1422                            ? WindowAnimation_activityOpenEnterAnimation
1423                            : WindowAnimation_activityOpenExitAnimation;
1424                    break;
1425                case TRANSIT_ACTIVITY_CLOSE:
1426                    animAttr = enter
1427                            ? WindowAnimation_activityCloseEnterAnimation
1428                            : WindowAnimation_activityCloseExitAnimation;
1429                    break;
1430                case TRANSIT_DOCK_TASK_FROM_RECENTS:
1431                case TRANSIT_TASK_OPEN:
1432                    animAttr = enter
1433                            ? WindowAnimation_taskOpenEnterAnimation
1434                            : WindowAnimation_taskOpenExitAnimation;
1435                    break;
1436                case TRANSIT_TASK_CLOSE:
1437                    animAttr = enter
1438                            ? WindowAnimation_taskCloseEnterAnimation
1439                            : WindowAnimation_taskCloseExitAnimation;
1440                    break;
1441                case TRANSIT_TASK_TO_FRONT:
1442                    animAttr = enter
1443                            ? WindowAnimation_taskToFrontEnterAnimation
1444                            : WindowAnimation_taskToFrontExitAnimation;
1445                    break;
1446                case TRANSIT_TASK_TO_BACK:
1447                    animAttr = enter
1448                            ? WindowAnimation_taskToBackEnterAnimation
1449                            : WindowAnimation_taskToBackExitAnimation;
1450                    break;
1451                case TRANSIT_WALLPAPER_OPEN:
1452                    animAttr = enter
1453                            ? WindowAnimation_wallpaperOpenEnterAnimation
1454                            : WindowAnimation_wallpaperOpenExitAnimation;
1455                    break;
1456                case TRANSIT_WALLPAPER_CLOSE:
1457                    animAttr = enter
1458                            ? WindowAnimation_wallpaperCloseEnterAnimation
1459                            : WindowAnimation_wallpaperCloseExitAnimation;
1460                    break;
1461                case TRANSIT_WALLPAPER_INTRA_OPEN:
1462                    animAttr = enter
1463                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
1464                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
1465                    break;
1466                case TRANSIT_WALLPAPER_INTRA_CLOSE:
1467                    animAttr = enter
1468                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
1469                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
1470                    break;
1471                case TRANSIT_TASK_OPEN_BEHIND:
1472                    animAttr = enter
1473                            ? WindowAnimation_launchTaskBehindSourceAnimation
1474                            : WindowAnimation_launchTaskBehindTargetAnimation;
1475            }
1476            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;
1477            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
1478                    "applyAnimation:"
1479                    + " anim=" + a
1480                    + " animAttr=0x" + Integer.toHexString(animAttr)
1481                    + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter
1482                    + " Callers=" + Debug.getCallers(3));
1483        }
1484        return a;
1485    }
1486
1487    int getAppStackClipMode() {
1488        return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
1489                ? STACK_CLIP_NONE
1490                : STACK_CLIP_AFTER_ANIM;
1491    }
1492
1493    void postAnimationCallback() {
1494        if (mNextAppTransitionCallback != null) {
1495            mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
1496                    mNextAppTransitionCallback));
1497            mNextAppTransitionCallback = null;
1498        }
1499    }
1500
1501    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
1502            IRemoteCallback startedCallback) {
1503        if (isTransitionSet()) {
1504            clear();
1505            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
1506            mNextAppTransitionPackage = packageName;
1507            mNextAppTransitionEnter = enterAnim;
1508            mNextAppTransitionExit = exitAnim;
1509            postAnimationCallback();
1510            mNextAppTransitionCallback = startedCallback;
1511        } else {
1512            postAnimationCallback();
1513        }
1514    }
1515
1516    void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
1517            int startHeight) {
1518        if (isTransitionSet()) {
1519            clear();
1520            mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
1521            putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
1522                    startY + startHeight, null);
1523            postAnimationCallback();
1524        }
1525    }
1526
1527    void overridePendingAppTransitionClipReveal(int startX, int startY,
1528                                                int startWidth, int startHeight) {
1529        if (isTransitionSet()) {
1530            clear();
1531            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
1532            putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
1533            postAnimationCallback();
1534        }
1535    }
1536
1537    void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
1538                                           IRemoteCallback startedCallback, boolean scaleUp) {
1539        if (isTransitionSet()) {
1540            clear();
1541            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
1542                    : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
1543            mNextAppTransitionScaleUp = scaleUp;
1544            putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
1545            postAnimationCallback();
1546            mNextAppTransitionCallback = startedCallback;
1547        } else {
1548            postAnimationCallback();
1549        }
1550    }
1551
1552    void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
1553            int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
1554        if (isTransitionSet()) {
1555            clear();
1556            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1557                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1558            mNextAppTransitionScaleUp = scaleUp;
1559            putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight,
1560                    srcThumb);
1561            postAnimationCallback();
1562            mNextAppTransitionCallback = startedCallback;
1563        } else {
1564            postAnimationCallback();
1565        }
1566    }
1567
1568    public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
1569            IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
1570            boolean scaleUp) {
1571        if (isTransitionSet()) {
1572            clear();
1573            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1574                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1575            mNextAppTransitionScaleUp = scaleUp;
1576            if (specs != null) {
1577                for (int i = 0; i < specs.length; i++) {
1578                    AppTransitionAnimationSpec spec = specs[i];
1579                    if (spec != null) {
1580                        mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec);
1581                        if (i == 0) {
1582                            // In full screen mode, the transition code depends on the default spec
1583                            // to be set.
1584                            Rect rect = spec.rect;
1585                            putDefaultNextAppTransitionCoordinates(rect.left, rect.top,
1586                                    rect.width(), rect.height(), spec.bitmap);
1587                        }
1588                    }
1589                }
1590            }
1591            postAnimationCallback();
1592            mNextAppTransitionCallback = onAnimationStartedCallback;
1593            mAnimationFinishedCallback = onAnimationFinishedCallback;
1594        } else {
1595            postAnimationCallback();
1596        }
1597    }
1598
1599    void overridePendingAppTransitionMultiThumbFuture(
1600            IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
1601            boolean scaleUp) {
1602        if (isTransitionSet()) {
1603            clear();
1604            mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
1605                    : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
1606            mNextAppTransitionAnimationsSpecsFuture = specsFuture;
1607            mNextAppTransitionScaleUp = scaleUp;
1608            mNextAppTransitionFutureCallback = callback;
1609        }
1610    }
1611
1612    void overrideInPlaceAppTransition(String packageName, int anim) {
1613        if (isTransitionSet()) {
1614            clear();
1615            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
1616            mNextAppTransitionPackage = packageName;
1617            mNextAppTransitionInPlace = anim;
1618        } else {
1619            postAnimationCallback();
1620        }
1621    }
1622
1623    /**
1624     * If a future is set for the app transition specs, fetch it in another thread.
1625     */
1626    private void fetchAppTransitionSpecsFromFuture() {
1627        if (mNextAppTransitionAnimationsSpecsFuture != null) {
1628            mNextAppTransitionAnimationsSpecsPending = true;
1629            final IAppTransitionAnimationSpecsFuture future
1630                    = mNextAppTransitionAnimationsSpecsFuture;
1631            mNextAppTransitionAnimationsSpecsFuture = null;
1632            mDefaultExecutor.execute(new Runnable() {
1633                @Override
1634                public void run() {
1635                    AppTransitionAnimationSpec[] specs = null;
1636                    try {
1637                        specs = future.get();
1638                    } catch (RemoteException e) {
1639                        Slog.w(TAG, "Failed to fetch app transition specs: " + e);
1640                    }
1641                    synchronized (mService.mWindowMap) {
1642                        mNextAppTransitionAnimationsSpecsPending = false;
1643                        overridePendingAppTransitionMultiThumb(specs,
1644                                mNextAppTransitionFutureCallback, null /* finishedCallback */,
1645                                mNextAppTransitionScaleUp);
1646                        mNextAppTransitionFutureCallback = null;
1647                        if (specs != null) {
1648                            mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
1649                        }
1650                    }
1651                    mService.requestTraversal();
1652                }
1653            });
1654        }
1655    }
1656
1657    @Override
1658    public String toString() {
1659        return "mNextAppTransition=" + appTransitionToString(mNextAppTransition);
1660    }
1661
1662    /**
1663     * Returns the human readable name of a window transition.
1664     *
1665     * @param transition The window transition.
1666     * @return The transition symbolic name.
1667     */
1668    public static String appTransitionToString(int transition) {
1669        switch (transition) {
1670            case TRANSIT_UNSET: {
1671                return "TRANSIT_UNSET";
1672            }
1673            case TRANSIT_NONE: {
1674                return "TRANSIT_NONE";
1675            }
1676            case TRANSIT_ACTIVITY_OPEN: {
1677                return "TRANSIT_ACTIVITY_OPEN";
1678            }
1679            case TRANSIT_ACTIVITY_CLOSE: {
1680                return "TRANSIT_ACTIVITY_CLOSE";
1681            }
1682            case TRANSIT_TASK_OPEN: {
1683                return "TRANSIT_TASK_OPEN";
1684            }
1685            case TRANSIT_TASK_CLOSE: {
1686                return "TRANSIT_TASK_CLOSE";
1687            }
1688            case TRANSIT_TASK_TO_FRONT: {
1689                return "TRANSIT_TASK_TO_FRONT";
1690            }
1691            case TRANSIT_TASK_TO_BACK: {
1692                return "TRANSIT_TASK_TO_BACK";
1693            }
1694            case TRANSIT_WALLPAPER_CLOSE: {
1695                return "TRANSIT_WALLPAPER_CLOSE";
1696            }
1697            case TRANSIT_WALLPAPER_OPEN: {
1698                return "TRANSIT_WALLPAPER_OPEN";
1699            }
1700            case TRANSIT_WALLPAPER_INTRA_OPEN: {
1701                return "TRANSIT_WALLPAPER_INTRA_OPEN";
1702            }
1703            case TRANSIT_WALLPAPER_INTRA_CLOSE: {
1704                return "TRANSIT_WALLPAPER_INTRA_CLOSE";
1705            }
1706            case TRANSIT_TASK_OPEN_BEHIND: {
1707                return "TRANSIT_TASK_OPEN_BEHIND";
1708            }
1709            case TRANSIT_ACTIVITY_RELAUNCH: {
1710                return "TRANSIT_ACTIVITY_RELAUNCH";
1711            }
1712            case TRANSIT_DOCK_TASK_FROM_RECENTS: {
1713                return "TRANSIT_DOCK_TASK_FROM_RECENTS";
1714            }
1715            default: {
1716                return "<UNKNOWN>";
1717            }
1718        }
1719    }
1720
1721    private String appStateToString() {
1722        switch (mAppTransitionState) {
1723            case APP_STATE_IDLE:
1724                return "APP_STATE_IDLE";
1725            case APP_STATE_READY:
1726                return "APP_STATE_READY";
1727            case APP_STATE_RUNNING:
1728                return "APP_STATE_RUNNING";
1729            case APP_STATE_TIMEOUT:
1730                return "APP_STATE_TIMEOUT";
1731            default:
1732                return "unknown state=" + mAppTransitionState;
1733        }
1734    }
1735
1736    private String transitTypeToString() {
1737        switch (mNextAppTransitionType) {
1738            case NEXT_TRANSIT_TYPE_NONE:
1739                return "NEXT_TRANSIT_TYPE_NONE";
1740            case NEXT_TRANSIT_TYPE_CUSTOM:
1741                return "NEXT_TRANSIT_TYPE_CUSTOM";
1742            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1743                return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE";
1744            case NEXT_TRANSIT_TYPE_SCALE_UP:
1745                return "NEXT_TRANSIT_TYPE_SCALE_UP";
1746            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1747                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP";
1748            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1749                return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN";
1750            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1751                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
1752            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
1753                return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
1754            default:
1755                return "unknown type=" + mNextAppTransitionType;
1756        }
1757    }
1758
1759    @Override
1760    public void dump(PrintWriter pw, String prefix) {
1761        pw.print(prefix); pw.println(this);
1762        pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString());
1763        if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) {
1764            pw.print(prefix); pw.print("mNextAppTransitionType=");
1765                    pw.println(transitTypeToString());
1766        }
1767        switch (mNextAppTransitionType) {
1768            case NEXT_TRANSIT_TYPE_CUSTOM:
1769                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1770                        pw.println(mNextAppTransitionPackage);
1771                pw.print(prefix); pw.print("mNextAppTransitionEnter=0x");
1772                        pw.print(Integer.toHexString(mNextAppTransitionEnter));
1773                        pw.print(" mNextAppTransitionExit=0x");
1774                        pw.println(Integer.toHexString(mNextAppTransitionExit));
1775                break;
1776            case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE:
1777                pw.print(prefix); pw.print("mNextAppTransitionPackage=");
1778                        pw.println(mNextAppTransitionPackage);
1779                pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x");
1780                        pw.print(Integer.toHexString(mNextAppTransitionInPlace));
1781                break;
1782            case NEXT_TRANSIT_TYPE_SCALE_UP: {
1783                getDefaultNextAppTransitionStartRect(mTmpRect);
1784                pw.print(prefix); pw.print("mNextAppTransitionStartX=");
1785                        pw.print(mTmpRect.left);
1786                        pw.print(" mNextAppTransitionStartY=");
1787                        pw.println(mTmpRect.top);
1788                pw.print(prefix); pw.print("mNextAppTransitionStartWidth=");
1789                        pw.print(mTmpRect.width());
1790                        pw.print(" mNextAppTransitionStartHeight=");
1791                        pw.println(mTmpRect.height());
1792                break;
1793            }
1794            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP:
1795            case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN:
1796            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP:
1797            case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: {
1798                pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec=");
1799                        pw.println(mDefaultNextAppTransitionAnimationSpec);
1800                pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs=");
1801                        pw.println(mNextAppTransitionAnimationsSpecs);
1802                pw.print(prefix); pw.print("mNextAppTransitionScaleUp=");
1803                        pw.println(mNextAppTransitionScaleUp);
1804                break;
1805            }
1806        }
1807        if (mNextAppTransitionCallback != null) {
1808            pw.print(prefix); pw.print("mNextAppTransitionCallback=");
1809                    pw.println(mNextAppTransitionCallback);
1810        }
1811    }
1812
1813    public void setCurrentUser(int newUserId) {
1814        mCurrentUserId = newUserId;
1815    }
1816
1817    /**
1818     * @return true if transition is not running and should not be skipped, false if transition is
1819     *         already running
1820     */
1821    boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) {
1822        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:"
1823                + " transit=" + appTransitionToString(transit)
1824                + " " + this
1825                + " alwaysKeepCurrent=" + alwaysKeepCurrent
1826                + " Callers=" + Debug.getCallers(3));
1827        if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) {
1828            setAppTransition(transit);
1829        } else if (!alwaysKeepCurrent) {
1830            if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
1831                // Opening a new task always supersedes a close for the anim.
1832                setAppTransition(transit);
1833            } else if (transit == TRANSIT_ACTIVITY_OPEN
1834                    && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
1835                // Opening a new activity always supersedes a close for the anim.
1836                setAppTransition(transit);
1837            }
1838        }
1839        boolean prepared = prepare();
1840        if (isTransitionSet()) {
1841            mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
1842            mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
1843        }
1844        return prepared;
1845    }
1846}
1847