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