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