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