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