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