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