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