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