AppTransition.java revision de63d441d7daf0503bcc6d5fd3f4f7efe06e23d3
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 17package com.android.server.wm; 18 19import static android.view.WindowManagerInternal.AppTransitionListener; 20import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation; 21import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; 22import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation; 23import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation; 24import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation; 25import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation; 26import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation; 27import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation; 28import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation; 29import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation; 30import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation; 31import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation; 32import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation; 33import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation; 34import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation; 35import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation; 36import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation; 37import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation; 38import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation; 39import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; 40import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation; 41import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; 42import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 43import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 44import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 45import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 46import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; 47import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; 48 49import android.annotation.Nullable; 50import android.content.Context; 51import android.content.res.Configuration; 52import android.graphics.Bitmap; 53import android.graphics.Rect; 54import android.os.Debug; 55import android.os.IBinder; 56import android.os.IRemoteCallback; 57import android.os.RemoteException; 58import android.util.ArraySet; 59import android.util.Slog; 60import android.util.SparseArray; 61import android.view.AppTransitionAnimationSpec; 62import android.view.IAppTransitionAnimationSpecsFuture; 63import android.view.WindowManager; 64import android.view.animation.AlphaAnimation; 65import android.view.animation.Animation; 66import android.view.animation.AnimationSet; 67import android.view.animation.AnimationUtils; 68import android.view.animation.ClipRectAnimation; 69import android.view.animation.Interpolator; 70import android.view.animation.PathInterpolator; 71import android.view.animation.ScaleAnimation; 72import android.view.animation.TranslateAnimation; 73 74import com.android.internal.util.DumpUtils.Dump; 75import com.android.server.AttributeCache; 76import com.android.server.wm.WindowManagerService.H; 77import com.android.server.wm.animation.ClipRectLRAnimation; 78import com.android.server.wm.animation.ClipRectTBAnimation; 79 80import java.io.PrintWriter; 81import java.util.ArrayList; 82import java.util.concurrent.ExecutorService; 83import java.util.concurrent.Executors; 84 85// State management of app transitions. When we are preparing for a 86// transition, mNextAppTransition will be the kind of transition to 87// perform or TRANSIT_NONE if we are not waiting. If we are waiting, 88// mOpeningApps and mClosingApps are the lists of tokens that will be 89// made visible or hidden at the next transition. 90public class AppTransition implements Dump { 91 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransition" : TAG_WM; 92 private static final int CLIP_REVEAL_TRANSLATION_Y_DP = 8; 93 94 /** Not set up for a transition. */ 95 public static final int TRANSIT_UNSET = -1; 96 /** No animation for transition. */ 97 public static final int TRANSIT_NONE = 0; 98 /** A window in a new activity is being opened on top of an existing one in the same task. */ 99 public static final int TRANSIT_ACTIVITY_OPEN = 6; 100 /** The window in the top-most activity is being closed to reveal the 101 * previous activity in the same task. */ 102 public static final int TRANSIT_ACTIVITY_CLOSE = 7; 103 /** A window in a new task is being opened on top of an existing one 104 * in another activity's task. */ 105 public static final int TRANSIT_TASK_OPEN = 8; 106 /** A window in the top-most activity is being closed to reveal the 107 * previous activity in a different task. */ 108 public static final int TRANSIT_TASK_CLOSE = 9; 109 /** A window in an existing task is being displayed on top of an existing one 110 * in another activity's task. */ 111 public static final int TRANSIT_TASK_TO_FRONT = 10; 112 /** A window in an existing task is being put below all other tasks. */ 113 public static final int TRANSIT_TASK_TO_BACK = 11; 114 /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that 115 * does, effectively closing the wallpaper. */ 116 public static final int TRANSIT_WALLPAPER_CLOSE = 12; 117 /** A window in a new activity that does have a wallpaper is being opened on one that didn't, 118 * effectively opening the wallpaper. */ 119 public static final int TRANSIT_WALLPAPER_OPEN = 13; 120 /** A window in a new activity is being opened on top of an existing one, and both are on top 121 * of the wallpaper. */ 122 public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14; 123 /** The window in the top-most activity is being closed to reveal the previous activity, and 124 * both are on top of the wallpaper. */ 125 public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15; 126 /** A window in a new task is being opened behind an existing one in another activity's task. 127 * The new window will show briefly and then be gone. */ 128 public static final int TRANSIT_TASK_OPEN_BEHIND = 16; 129 /** A window in a task is being animated in-place. */ 130 public static final int TRANSIT_TASK_IN_PLACE = 17; 131 /** An activity is being relaunched (e.g. due to configuration change). */ 132 public static final int TRANSIT_ACTIVITY_RELAUNCH = 18; 133 /** A task is being docked from recents. */ 134 public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19; 135 136 /** Fraction of animation at which the recents thumbnail stays completely transparent */ 137 private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f; 138 /** Fraction of animation at which the recents thumbnail becomes completely transparent */ 139 private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f; 140 141 static final int DEFAULT_APP_TRANSITION_DURATION = 336; 142 143 /** Interpolator to be used for animations that respond directly to a touch */ 144 static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = 145 new PathInterpolator(0.3f, 0f, 0.1f, 1f); 146 147 /** 148 * Maximum duration for the clip reveal animation. This is used when there is a lot of movement 149 * involved, to make it more understandable. 150 */ 151 private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420; 152 private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336; 153 private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336; 154 private static final long APP_TRANSITION_TIMEOUT_MS = 5000; 155 156 private final Context mContext; 157 private final WindowManagerService mService; 158 159 private int mNextAppTransition = TRANSIT_UNSET; 160 161 private static final int NEXT_TRANSIT_TYPE_NONE = 0; 162 private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1; 163 private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2; 164 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3; 165 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4; 166 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP = 5; 167 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN = 6; 168 private static final int NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE = 7; 169 private static final int NEXT_TRANSIT_TYPE_CLIP_REVEAL = 8; 170 private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; 171 172 // These are the possible states for the enter/exit activities during a thumbnail transition 173 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; 174 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; 175 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; 176 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; 177 178 private String mNextAppTransitionPackage; 179 // Used for thumbnail transitions. True if we're scaling up, false if scaling down 180 private boolean mNextAppTransitionScaleUp; 181 private IRemoteCallback mNextAppTransitionCallback; 182 private IRemoteCallback mNextAppTransitionFutureCallback; 183 private IRemoteCallback mAnimationFinishedCallback; 184 private int mNextAppTransitionEnter; 185 private int mNextAppTransitionExit; 186 private int mNextAppTransitionInPlace; 187 188 // Keyed by task id. 189 private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs 190 = new SparseArray<>(); 191 private IAppTransitionAnimationSpecsFuture mNextAppTransitionAnimationsSpecsFuture; 192 private boolean mNextAppTransitionAnimationsSpecsPending; 193 private AppTransitionAnimationSpec mDefaultNextAppTransitionAnimationSpec; 194 195 private Rect mNextAppTransitionInsets = new Rect(); 196 197 private Rect mTmpFromClipRect = new Rect(); 198 private Rect mTmpToClipRect = new Rect(); 199 200 private final Rect mTmpRect = new Rect(); 201 202 private final static int APP_STATE_IDLE = 0; 203 private final static int APP_STATE_READY = 1; 204 private final static int APP_STATE_RUNNING = 2; 205 private final static int APP_STATE_TIMEOUT = 3; 206 private int mAppTransitionState = APP_STATE_IDLE; 207 208 private final int mConfigShortAnimTime; 209 private final Interpolator mDecelerateInterpolator; 210 private final Interpolator mThumbnailFadeInInterpolator; 211 private final Interpolator mThumbnailFadeOutInterpolator; 212 private final Interpolator mLinearOutSlowInInterpolator; 213 private final Interpolator mFastOutLinearInInterpolator; 214 private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f); 215 216 private final int mClipRevealTranslationY; 217 218 private int mCurrentUserId = 0; 219 private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION; 220 221 private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>(); 222 private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor(); 223 224 private int mLastClipRevealMaxTranslation; 225 private boolean mLastHadClipReveal; 226 227 AppTransition(Context context, WindowManagerService service) { 228 mContext = context; 229 mService = service; 230 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 231 com.android.internal.R.interpolator.linear_out_slow_in); 232 mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, 233 com.android.internal.R.interpolator.fast_out_linear_in); 234 mConfigShortAnimTime = context.getResources().getInteger( 235 com.android.internal.R.integer.config_shortAnimTime); 236 mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, 237 com.android.internal.R.interpolator.decelerate_cubic); 238 mThumbnailFadeInInterpolator = new Interpolator() { 239 @Override 240 public float getInterpolation(float input) { 241 // Linear response for first fraction, then complete after that. 242 if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) { 243 return 0f; 244 } 245 float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) / 246 (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION); 247 return mFastOutLinearInInterpolator.getInterpolation(t); 248 } 249 }; 250 mThumbnailFadeOutInterpolator = new Interpolator() { 251 @Override 252 public float getInterpolation(float input) { 253 // Linear response for first fraction, then complete after that. 254 if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { 255 float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION; 256 return mLinearOutSlowInInterpolator.getInterpolation(t); 257 } 258 return 1f; 259 } 260 }; 261 mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP 262 * mContext.getResources().getDisplayMetrics().density); 263 } 264 265 boolean isTransitionSet() { 266 return mNextAppTransition != TRANSIT_UNSET; 267 } 268 269 boolean isTransitionEqual(int transit) { 270 return mNextAppTransition == transit; 271 } 272 273 int getAppTransition() { 274 return mNextAppTransition; 275 } 276 277 private void setAppTransition(int transit) { 278 mNextAppTransition = transit; 279 } 280 281 boolean isReady() { 282 return mAppTransitionState == APP_STATE_READY 283 || mAppTransitionState == APP_STATE_TIMEOUT; 284 } 285 286 void setReady() { 287 mAppTransitionState = APP_STATE_READY; 288 fetchAppTransitionSpecsFromFuture(); 289 } 290 291 boolean isRunning() { 292 return mAppTransitionState == APP_STATE_RUNNING; 293 } 294 295 void setIdle() { 296 mAppTransitionState = APP_STATE_IDLE; 297 } 298 299 boolean isTimeout() { 300 return mAppTransitionState == APP_STATE_TIMEOUT; 301 } 302 303 void setTimeout() { 304 mAppTransitionState = APP_STATE_TIMEOUT; 305 } 306 307 Bitmap getAppTransitionThumbnailHeader(int taskId) { 308 AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId); 309 if (spec == null) { 310 spec = mDefaultNextAppTransitionAnimationSpec; 311 } 312 return spec != null ? spec.bitmap : null; 313 } 314 315 /** Returns whether the next thumbnail transition is aspect scaled up. */ 316 boolean isNextThumbnailTransitionAspectScaled() { 317 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || 318 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 319 } 320 321 /** Returns whether the next thumbnail transition is scaling up. */ 322 boolean isNextThumbnailTransitionScaleUp() { 323 return mNextAppTransitionScaleUp; 324 } 325 326 boolean isNextAppTransitionThumbnailUp() { 327 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || 328 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP; 329 } 330 331 boolean isNextAppTransitionThumbnailDown() { 332 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN || 333 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 334 } 335 336 /** 337 * @return true if and only if we are currently fetching app transition specs from the future 338 * passed into {@link #overridePendingAppTransitionMultiThumbFuture} 339 */ 340 boolean isFetchingAppTransitionsSpecs() { 341 return mNextAppTransitionAnimationsSpecsPending; 342 } 343 344 private boolean prepare() { 345 if (!isRunning()) { 346 mAppTransitionState = APP_STATE_IDLE; 347 notifyAppTransitionPendingLocked(); 348 mLastHadClipReveal = false; 349 mLastClipRevealMaxTranslation = 0; 350 mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION; 351 return true; 352 } 353 return false; 354 } 355 356 void goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator, 357 ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps) { 358 mNextAppTransition = TRANSIT_UNSET; 359 mAppTransitionState = APP_STATE_RUNNING; 360 notifyAppTransitionStartingLocked( 361 topOpeningAppAnimator != null ? topOpeningAppAnimator.mAppToken.token : null, 362 topClosingAppAnimator != null ? topClosingAppAnimator.mAppToken.token : null, 363 topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null, 364 topClosingAppAnimator != null ? topClosingAppAnimator.animation : null); 365 mService.getDefaultDisplayContentLocked().getDockedDividerController() 366 .notifyAppTransitionStarting(openingApps, closingApps); 367 } 368 369 void clear() { 370 mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; 371 mNextAppTransitionPackage = null; 372 mNextAppTransitionAnimationsSpecs.clear(); 373 mNextAppTransitionAnimationsSpecsFuture = null; 374 mDefaultNextAppTransitionAnimationSpec = null; 375 mAnimationFinishedCallback = null; 376 } 377 378 void freeze() { 379 setAppTransition(AppTransition.TRANSIT_UNSET); 380 clear(); 381 setReady(); 382 notifyAppTransitionCancelledLocked(); 383 } 384 385 void registerListenerLocked(AppTransitionListener listener) { 386 mListeners.add(listener); 387 } 388 389 public void notifyAppTransitionFinishedLocked(IBinder token) { 390 for (int i = 0; i < mListeners.size(); i++) { 391 mListeners.get(i).onAppTransitionFinishedLocked(token); 392 } 393 } 394 395 private void notifyAppTransitionPendingLocked() { 396 for (int i = 0; i < mListeners.size(); i++) { 397 mListeners.get(i).onAppTransitionPendingLocked(); 398 } 399 } 400 401 private void notifyAppTransitionCancelledLocked() { 402 for (int i = 0; i < mListeners.size(); i++) { 403 mListeners.get(i).onAppTransitionCancelledLocked(); 404 } 405 } 406 407 private void notifyAppTransitionStartingLocked(IBinder openToken, 408 IBinder closeToken, Animation openAnimation, Animation closeAnimation) { 409 for (int i = 0; i < mListeners.size(); i++) { 410 mListeners.get(i).onAppTransitionStartingLocked(openToken, closeToken, openAnimation, 411 closeAnimation); 412 } 413 } 414 415 private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) { 416 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg=" 417 + (lp != null ? lp.packageName : null) 418 + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null)); 419 if (lp != null && lp.windowAnimations != 0) { 420 // If this is a system resource, don't try to load it from the 421 // application resources. It is nice to avoid loading application 422 // resources if we can. 423 String packageName = lp.packageName != null ? lp.packageName : "android"; 424 int resId = lp.windowAnimations; 425 if ((resId&0xFF000000) == 0x01000000) { 426 packageName = "android"; 427 } 428 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" 429 + packageName); 430 return AttributeCache.instance().get(packageName, resId, 431 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId); 432 } 433 return null; 434 } 435 436 private AttributeCache.Entry getCachedAnimations(String packageName, int resId) { 437 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package=" 438 + packageName + " resId=0x" + Integer.toHexString(resId)); 439 if (packageName != null) { 440 if ((resId&0xFF000000) == 0x01000000) { 441 packageName = "android"; 442 } 443 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" 444 + packageName); 445 return AttributeCache.instance().get(packageName, resId, 446 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId); 447 } 448 return null; 449 } 450 451 Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) { 452 int anim = 0; 453 Context context = mContext; 454 if (animAttr >= 0) { 455 AttributeCache.Entry ent = getCachedAnimations(lp); 456 if (ent != null) { 457 context = ent.context; 458 anim = ent.array.getResourceId(animAttr, 0); 459 } 460 } 461 if (anim != 0) { 462 return AnimationUtils.loadAnimation(context, anim); 463 } 464 return null; 465 } 466 467 Animation loadAnimationRes(WindowManager.LayoutParams lp, int resId) { 468 Context context = mContext; 469 if (resId >= 0) { 470 AttributeCache.Entry ent = getCachedAnimations(lp); 471 if (ent != null) { 472 context = ent.context; 473 } 474 return AnimationUtils.loadAnimation(context, resId); 475 } 476 return null; 477 } 478 479 private Animation loadAnimationRes(String packageName, int resId) { 480 int anim = 0; 481 Context context = mContext; 482 if (resId >= 0) { 483 AttributeCache.Entry ent = getCachedAnimations(packageName, resId); 484 if (ent != null) { 485 context = ent.context; 486 anim = resId; 487 } 488 } 489 if (anim != 0) { 490 return AnimationUtils.loadAnimation(context, anim); 491 } 492 return null; 493 } 494 495 /** 496 * Compute the pivot point for an animation that is scaling from a small 497 * rect on screen to a larger rect. The pivot point varies depending on 498 * the distance between the inner and outer edges on both sides. This 499 * function computes the pivot point for one dimension. 500 * @param startPos Offset from left/top edge of outer rectangle to 501 * left/top edge of inner rectangle. 502 * @param finalScale The scaling factor between the size of the outer 503 * and inner rectangles. 504 */ 505 private static float computePivot(int startPos, float finalScale) { 506 507 /* 508 Theorem of intercepting lines: 509 510 + + +-----------------------------------------------+ 511 | | | | 512 | | | | 513 | | | | 514 | | | | 515 x | y | | | 516 | | | | 517 | | | | 518 | | | | 519 | | | | 520 | + | +--------------------+ | 521 | | | | | 522 | | | | | 523 | | | | | 524 | | | | | 525 | | | | | 526 | | | | | 527 | | | | | 528 | | | | | 529 | | | | | 530 | | | | | 531 | | | | | 532 | | | | | 533 | | | | | 534 | | | | | 535 | | | | | 536 | | | | | 537 | | | | | 538 | | +--------------------+ | 539 | | | 540 | | | 541 | | | 542 | | | 543 | | | 544 | | | 545 | | | 546 | +-----------------------------------------------+ 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 + ++ 557 p ++ 558 559 scale = (x - y) / x 560 <=> x = -y / (scale - 1) 561 */ 562 final float denom = finalScale-1; 563 if (Math.abs(denom) < .0001f) { 564 return startPos; 565 } 566 return -startPos / denom; 567 } 568 569 private Animation createScaleUpAnimationLocked(int transit, boolean enter, 570 Rect containingFrame) { 571 Animation a; 572 getDefaultNextAppTransitionStartRect(mTmpRect); 573 final int appWidth = containingFrame.width(); 574 final int appHeight = containingFrame.height(); 575 if (enter) { 576 // Entering app zooms out from the center of the initial rect. 577 float scaleW = mTmpRect.width() / (float) appWidth; 578 float scaleH = mTmpRect.height() / (float) appHeight; 579 Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, 580 computePivot(mTmpRect.left, scaleW), 581 computePivot(mTmpRect.right, scaleH)); 582 scale.setInterpolator(mDecelerateInterpolator); 583 584 Animation alpha = new AlphaAnimation(0, 1); 585 alpha.setInterpolator(mThumbnailFadeOutInterpolator); 586 587 AnimationSet set = new AnimationSet(false); 588 set.addAnimation(scale); 589 set.addAnimation(alpha); 590 set.setDetachWallpaper(true); 591 a = set; 592 } else if (transit == TRANSIT_WALLPAPER_INTRA_OPEN || 593 transit == TRANSIT_WALLPAPER_INTRA_CLOSE) { 594 // If we are on top of the wallpaper, we need an animation that 595 // correctly handles the wallpaper staying static behind all of 596 // the animated elements. To do this, will just have the existing 597 // element fade out. 598 a = new AlphaAnimation(1, 0); 599 a.setDetachWallpaper(true); 600 } else { 601 // For normal animations, the exiting element just holds in place. 602 a = new AlphaAnimation(1, 1); 603 } 604 605 // Pick the desired duration. If this is an inter-activity transition, 606 // it is the standard duration for that. Otherwise we use the longer 607 // task transition duration. 608 final long duration; 609 switch (transit) { 610 case TRANSIT_ACTIVITY_OPEN: 611 case TRANSIT_ACTIVITY_CLOSE: 612 duration = mConfigShortAnimTime; 613 break; 614 default: 615 duration = DEFAULT_APP_TRANSITION_DURATION; 616 break; 617 } 618 a.setDuration(duration); 619 a.setFillAfter(true); 620 a.setInterpolator(mDecelerateInterpolator); 621 a.initialize(appWidth, appHeight, appWidth, appHeight); 622 return a; 623 } 624 625 private void getDefaultNextAppTransitionStartRect(Rect rect) { 626 if (mDefaultNextAppTransitionAnimationSpec == null || 627 mDefaultNextAppTransitionAnimationSpec.rect == null) { 628 Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable()); 629 rect.setEmpty(); 630 } else { 631 rect.set(mDefaultNextAppTransitionAnimationSpec.rect); 632 } 633 } 634 635 void getNextAppTransitionStartRect(int taskId, Rect rect) { 636 AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(taskId); 637 if (spec == null) { 638 spec = mDefaultNextAppTransitionAnimationSpec; 639 } 640 if (spec == null || spec.rect == null) { 641 Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available", 642 new Throwable()); 643 rect.setEmpty(); 644 } else { 645 rect.set(spec.rect); 646 } 647 } 648 649 private void putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height, 650 Bitmap bitmap) { 651 mDefaultNextAppTransitionAnimationSpec = new AppTransitionAnimationSpec(-1 /* taskId */, 652 bitmap, new Rect(left, top, left + width, top + height)); 653 } 654 655 /** 656 * @return the duration of the last clip reveal animation 657 */ 658 long getLastClipRevealTransitionDuration() { 659 return mLastClipRevealTransitionDuration; 660 } 661 662 /** 663 * @return the maximum distance the app surface is traveling of the last clip reveal animation 664 */ 665 int getLastClipRevealMaxTranslation() { 666 return mLastClipRevealMaxTranslation; 667 } 668 669 /** 670 * @return true if in the last app transition had a clip reveal animation, false otherwise 671 */ 672 boolean hadClipRevealAnimation() { 673 return mLastHadClipReveal; 674 } 675 676 /** 677 * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that 678 * the start rect is outside of the target rect, and there is a lot of movement going on. 679 * 680 * @param cutOff whether the start rect was not fully contained by the end rect 681 * @param translationX the total translation the surface moves in x direction 682 * @param translationY the total translation the surfaces moves in y direction 683 * @param displayFrame our display frame 684 * 685 * @return the duration of the clip reveal animation, in milliseconds 686 */ 687 private long calculateClipRevealTransitionDuration(boolean cutOff, float translationX, 688 float translationY, Rect displayFrame) { 689 if (!cutOff) { 690 return DEFAULT_APP_TRANSITION_DURATION; 691 } 692 final float fraction = Math.max(Math.abs(translationX) / displayFrame.width(), 693 Math.abs(translationY) / displayFrame.height()); 694 return (long) (DEFAULT_APP_TRANSITION_DURATION + fraction * 695 (MAX_CLIP_REVEAL_TRANSITION_DURATION - DEFAULT_APP_TRANSITION_DURATION)); 696 } 697 698 private Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame, 699 Rect displayFrame) { 700 final Animation anim; 701 if (enter) { 702 final int appWidth = appFrame.width(); 703 final int appHeight = appFrame.height(); 704 705 // mTmpRect will contain an area around the launcher icon that was pressed. We will 706 // clip reveal from that area in the final area of the app. 707 getDefaultNextAppTransitionStartRect(mTmpRect); 708 709 float t = 0f; 710 if (appHeight > 0) { 711 t = (float) mTmpRect.top / displayFrame.height(); 712 } 713 int translationY = mClipRevealTranslationY + (int)(displayFrame.height() / 7f * t); 714 int translationX = 0; 715 int translationYCorrection = translationY; 716 int centerX = mTmpRect.centerX(); 717 int centerY = mTmpRect.centerY(); 718 int halfWidth = mTmpRect.width() / 2; 719 int halfHeight = mTmpRect.height() / 2; 720 int clipStartX = centerX - halfWidth - appFrame.left; 721 int clipStartY = centerY - halfHeight - appFrame.top; 722 boolean cutOff = false; 723 724 // If the starting rectangle is fully or partially outside of the target rectangle, we 725 // need to start the clipping at the edge and then achieve the rest with translation 726 // and extending the clip rect from that edge. 727 if (appFrame.top > centerY - halfHeight) { 728 translationY = (centerY - halfHeight) - appFrame.top; 729 translationYCorrection = 0; 730 clipStartY = 0; 731 cutOff = true; 732 } 733 if (appFrame.left > centerX - halfWidth) { 734 translationX = (centerX - halfWidth) - appFrame.left; 735 clipStartX = 0; 736 cutOff = true; 737 } 738 if (appFrame.right < centerX + halfWidth) { 739 translationX = (centerX + halfWidth) - appFrame.right; 740 clipStartX = appWidth - mTmpRect.width(); 741 cutOff = true; 742 } 743 final long duration = calculateClipRevealTransitionDuration(cutOff, translationX, 744 translationY, displayFrame); 745 746 // Clip third of the from size of launch icon, expand to full width/height 747 Animation clipAnimLR = new ClipRectLRAnimation( 748 clipStartX, clipStartX + mTmpRect.width(), 0, appWidth); 749 clipAnimLR.setInterpolator(mClipHorizontalInterpolator); 750 clipAnimLR.setDuration((long) (duration / 2.5f)); 751 752 TranslateAnimation translate = new TranslateAnimation(translationX, 0, translationY, 0); 753 translate.setInterpolator(cutOff ? TOUCH_RESPONSE_INTERPOLATOR 754 : mLinearOutSlowInInterpolator); 755 translate.setDuration(duration); 756 757 Animation clipAnimTB = new ClipRectTBAnimation( 758 clipStartY, clipStartY + mTmpRect.height(), 759 0, appHeight, 760 translationYCorrection, 0, 761 mLinearOutSlowInInterpolator); 762 clipAnimTB.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 763 clipAnimTB.setDuration(duration); 764 765 // Quick fade-in from icon to app window 766 final long alphaDuration = duration / 4; 767 AlphaAnimation alpha = new AlphaAnimation(0.5f, 1); 768 alpha.setDuration(alphaDuration); 769 alpha.setInterpolator(mLinearOutSlowInInterpolator); 770 771 AnimationSet set = new AnimationSet(false); 772 set.addAnimation(clipAnimLR); 773 set.addAnimation(clipAnimTB); 774 set.addAnimation(translate); 775 set.addAnimation(alpha); 776 set.setZAdjustment(Animation.ZORDER_TOP); 777 set.initialize(appWidth, appHeight, appWidth, appHeight); 778 anim = set; 779 mLastHadClipReveal = true; 780 mLastClipRevealTransitionDuration = duration; 781 782 // If the start rect was full inside the target rect (cutOff == false), we don't need 783 // to store the translation, because it's only used if cutOff == true. 784 mLastClipRevealMaxTranslation = cutOff 785 ? Math.max(Math.abs(translationY), Math.abs(translationX)) : 0; 786 } else { 787 final long duration; 788 switch (transit) { 789 case TRANSIT_ACTIVITY_OPEN: 790 case TRANSIT_ACTIVITY_CLOSE: 791 duration = mConfigShortAnimTime; 792 break; 793 default: 794 duration = DEFAULT_APP_TRANSITION_DURATION; 795 break; 796 } 797 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN || 798 transit == TRANSIT_WALLPAPER_INTRA_CLOSE) { 799 // If we are on top of the wallpaper, we need an animation that 800 // correctly handles the wallpaper staying static behind all of 801 // the animated elements. To do this, will just have the existing 802 // element fade out. 803 anim = new AlphaAnimation(1, 0); 804 anim.setDetachWallpaper(true); 805 } else { 806 // For normal animations, the exiting element just holds in place. 807 anim = new AlphaAnimation(1, 1); 808 } 809 anim.setInterpolator(mDecelerateInterpolator); 810 anim.setDuration(duration); 811 anim.setFillAfter(true); 812 } 813 return anim; 814 } 815 816 /** 817 * Prepares the specified animation with a standard duration, interpolator, etc. 818 */ 819 Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight, 820 int duration, Interpolator interpolator) { 821 if (duration > 0) { 822 a.setDuration(duration); 823 } 824 a.setFillAfter(true); 825 a.setInterpolator(interpolator); 826 a.initialize(appWidth, appHeight, appWidth, appHeight); 827 return a; 828 } 829 830 /** 831 * Prepares the specified animation with a standard duration, interpolator, etc. 832 */ 833 Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { 834 // Pick the desired duration. If this is an inter-activity transition, 835 // it is the standard duration for that. Otherwise we use the longer 836 // task transition duration. 837 final int duration; 838 switch (transit) { 839 case TRANSIT_ACTIVITY_OPEN: 840 case TRANSIT_ACTIVITY_CLOSE: 841 duration = mConfigShortAnimTime; 842 break; 843 default: 844 duration = DEFAULT_APP_TRANSITION_DURATION; 845 break; 846 } 847 return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration, 848 mDecelerateInterpolator); 849 } 850 851 /** 852 * Return the current thumbnail transition state. 853 */ 854 int getThumbnailTransitionState(boolean enter) { 855 if (enter) { 856 if (mNextAppTransitionScaleUp) { 857 return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; 858 } else { 859 return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; 860 } 861 } else { 862 if (mNextAppTransitionScaleUp) { 863 return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; 864 } else { 865 return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; 866 } 867 } 868 } 869 870 /** 871 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity 872 * when a thumbnail is specified with the pending animation override. 873 */ 874 Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets, 875 Bitmap thumbnailHeader, final int taskId) { 876 Animation a; 877 final int thumbWidthI = thumbnailHeader.getWidth(); 878 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 879 final int thumbHeightI = thumbnailHeader.getHeight(); 880 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 881 final int appWidth = appRect.width(); 882 883 float scaleW = appWidth / thumbWidth; 884 float unscaledHeight = thumbHeight * scaleW; 885 getNextAppTransitionStartRect(taskId, mTmpRect); 886 final float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f; 887 final float toY = appRect.top + -unscaledStartY; 888 if (mNextAppTransitionScaleUp) { 889 // Animation up from the thumbnail to the full screen 890 Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, 891 mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f)); 892 scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 893 scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); 894 Animation alpha = new AlphaAnimation(1f, 0f); 895 alpha.setInterpolator(mThumbnailFadeOutInterpolator); 896 alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION); 897 final float toX = appRect.left + appRect.width() / 2 - 898 (mTmpRect.left + thumbWidth / 2); 899 Animation translate = new TranslateAnimation(0, toX, 0, toY); 900 translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 901 translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); 902 903 float thumbScale = thumbWidth / (appWidth - (contentInsets != null 904 ? contentInsets.left - contentInsets.right : 0)); 905 906 mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI); 907 mTmpToClipRect.set(appRect); 908 909 // Containing frame is in screen space, but we need the clip rect in the 910 // app space. 911 mTmpToClipRect.offsetTo(0, 0); 912 mTmpToClipRect.right = (int) (mTmpToClipRect.right * thumbScale); 913 mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom * thumbScale); 914 915 if (contentInsets != null) { 916 mTmpToClipRect.inset((int) (-contentInsets.left * thumbScale), 917 (int) (-contentInsets.top * thumbScale), 918 (int) (-contentInsets.right * thumbScale), 919 (int) (-contentInsets.bottom * thumbScale)); 920 } 921 922 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 923 clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 924 clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); 925 926 // This AnimationSet uses the Interpolators assigned above. 927 AnimationSet set = new AnimationSet(false); 928 set.addAnimation(scale); 929 set.addAnimation(alpha); 930 set.addAnimation(translate); 931 set.addAnimation(clipAnim); 932 a = set; 933 } else { 934 // Animation down from the full screen to the thumbnail 935 Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, 936 mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f)); 937 scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 938 scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); 939 Animation alpha = new AlphaAnimation(0f, 1f); 940 alpha.setInterpolator(mThumbnailFadeInInterpolator); 941 alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION); 942 final float toX = appRect.left + appRect.width() / 2 - 943 (mTmpRect.left + thumbWidth / 2); 944 Animation translate = new TranslateAnimation(toX, 0, toY, 0); 945 translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); 946 translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); 947 948 // This AnimationSet uses the Interpolators assigned above. 949 AnimationSet set = new AnimationSet(false); 950 set.addAnimation(scale); 951 set.addAnimation(alpha); 952 set.addAnimation(translate); 953 a = set; 954 955 } 956 return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0, 957 TOUCH_RESPONSE_INTERPOLATOR); 958 } 959 960 /** 961 * This alternate animation is created when we are doing a thumbnail transition, for the 962 * activity that is leaving, and the activity that is entering. 963 */ 964 Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, 965 int orientation, int transit, Rect containingFrame, Rect contentInsets, 966 @Nullable Rect surfaceInsets, boolean freeform, int taskId) { 967 Animation a; 968 final int appWidth = containingFrame.width(); 969 final int appHeight = containingFrame.height(); 970 getDefaultNextAppTransitionStartRect(mTmpRect); 971 final int thumbWidthI = mTmpRect.width(); 972 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 973 final int thumbHeightI = mTmpRect.height(); 974 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 975 final int thumbStartX = mTmpRect.left - containingFrame.left; 976 final int thumbStartY = mTmpRect.top - containingFrame.top; 977 978 // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions 979 float scale = 1f; 980 int scaledTopDecor = 0; 981 982 switch (thumbTransitState) { 983 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { 984 if (freeform) { 985 a = createAspectScaledThumbnailEnterFreeformAnimationLocked( 986 containingFrame, surfaceInsets, taskId); 987 } else { 988 AnimationSet set = new AnimationSet(true); 989 990 // In portrait, we scale to fit the width 991 mTmpFromClipRect.set(containingFrame); 992 mTmpToClipRect.set(containingFrame); 993 994 // Containing frame is in screen space, but we need the clip rect in the 995 // app space. 996 mTmpFromClipRect.offsetTo(0, 0); 997 mTmpToClipRect.offsetTo(0, 0); 998 999 // Exclude insets region from the source clip. 1000 mTmpFromClipRect.inset(contentInsets); 1001 mNextAppTransitionInsets.set(contentInsets); 1002 1003 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 1004 // We scale the width and clip to the top/left square 1005 scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right); 1006 scaledTopDecor = (int) (scale * contentInsets.top); 1007 int unscaledThumbHeight = (int) (thumbHeight / scale); 1008 mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight; 1009 1010 Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1, 1011 computePivot(mTmpRect.left - containingFrame.left, scale), 1012 computePivot(mTmpRect.top - containingFrame.top, scale)); 1013 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 1014 Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0); 1015 1016 set.addAnimation(clipAnim); 1017 set.addAnimation(scaleAnim); 1018 set.addAnimation(translateAnim); 1019 1020 } else { 1021 // In landscape, we don't scale at all and only crop 1022 mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI; 1023 mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI; 1024 1025 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 1026 Animation translateAnim = new TranslateAnimation(thumbStartX, 0, 1027 thumbStartY - contentInsets.top, 0); 1028 1029 set.addAnimation(clipAnim); 1030 set.addAnimation(translateAnim); 1031 } 1032 1033 a = set; 1034 a.setZAdjustment(Animation.ZORDER_TOP); 1035 } 1036 break; 1037 } 1038 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 1039 // Previous app window during the scale up 1040 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1041 // Fade out the source activity if we are animating to a wallpaper 1042 // activity. 1043 a = new AlphaAnimation(1, 0); 1044 } else { 1045 a = new AlphaAnimation(1, 1); 1046 } 1047 break; 1048 } 1049 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 1050 // Target app window during the scale down 1051 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1052 // Fade in the destination activity if we are animating from a wallpaper 1053 // activity. 1054 a = new AlphaAnimation(0, 1); 1055 } else { 1056 a = new AlphaAnimation(1, 1); 1057 } 1058 break; 1059 } 1060 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 1061 // App window scaling down from full screen 1062 if (freeform) { 1063 a = createAspectScaledThumbnailExitFreeformAnimationLocked( 1064 containingFrame, surfaceInsets, taskId); 1065 } else { 1066 AnimationSet set = new AnimationSet(true); 1067 mTmpFromClipRect.set(containingFrame); 1068 mTmpToClipRect.set(containingFrame); 1069 1070 // Containing frame is in screen space, but we need the clip rect in the 1071 // app space. 1072 mTmpFromClipRect.offsetTo(0, 0); 1073 mTmpToClipRect.offsetTo(0, 0); 1074 1075 // Exclude insets region from the target clip. 1076 mTmpToClipRect.inset(contentInsets); 1077 mNextAppTransitionInsets.set(contentInsets); 1078 1079 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 1080 // We scale the width and clip to the top/left square 1081 scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right); 1082 scaledTopDecor = (int) (scale * contentInsets.top); 1083 int unscaledThumbHeight = (int) (thumbHeight / scale); 1084 mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight; 1085 1086 Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale, 1087 computePivot(mTmpRect.left - containingFrame.left, scale), 1088 computePivot(mTmpRect.top - containingFrame.top, scale)); 1089 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 1090 Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor); 1091 1092 set.addAnimation(clipAnim); 1093 set.addAnimation(scaleAnim); 1094 set.addAnimation(translateAnim); 1095 1096 } else { 1097 // In landscape, we don't scale at all and only crop 1098 mTmpToClipRect.bottom = mTmpToClipRect.top + thumbHeightI; 1099 mTmpToClipRect.right = mTmpToClipRect.left + thumbWidthI; 1100 1101 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 1102 Animation translateAnim = new TranslateAnimation(0, thumbStartX, 0, 1103 thumbStartY - contentInsets.top); 1104 1105 set.addAnimation(clipAnim); 1106 set.addAnimation(translateAnim); 1107 } 1108 1109 a = set; 1110 a.setZAdjustment(Animation.ZORDER_TOP); 1111 } 1112 break; 1113 } 1114 default: 1115 throw new RuntimeException("Invalid thumbnail transition state"); 1116 } 1117 1118 int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION, 1119 THUMBNAIL_APP_TRANSITION_DURATION); 1120 return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration, 1121 TOUCH_RESPONSE_INTERPOLATOR); 1122 } 1123 1124 private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame, 1125 @Nullable Rect surfaceInsets, int taskId) { 1126 getNextAppTransitionStartRect(taskId, mTmpRect); 1127 return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets, 1128 true); 1129 } 1130 1131 private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame, 1132 @Nullable Rect surfaceInsets, int taskId) { 1133 getNextAppTransitionStartRect(taskId, mTmpRect); 1134 return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets, 1135 false); 1136 } 1137 1138 private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame, 1139 Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) { 1140 final float sourceWidth = sourceFrame.width(); 1141 final float sourceHeight = sourceFrame.height(); 1142 final float destWidth = destFrame.width(); 1143 final float destHeight = destFrame.height(); 1144 final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth; 1145 final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight; 1146 AnimationSet set = new AnimationSet(true); 1147 final int surfaceInsetsH = surfaceInsets == null 1148 ? 0 : surfaceInsets.left + surfaceInsets.right; 1149 final int surfaceInsetsV = surfaceInsets == null 1150 ? 0 : surfaceInsets.top + surfaceInsets.bottom; 1151 // We want the scaling to happen from the center of the surface. In order to achieve that, 1152 // we need to account for surface insets that will be used to enlarge the surface. 1153 final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2; 1154 final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2; 1155 final ScaleAnimation scale = enter ? 1156 new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter) 1157 : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter); 1158 final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2; 1159 final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2; 1160 final int destHCenter = destFrame.left + destFrame.width() / 2; 1161 final int destVCenter = destFrame.top + destFrame.height() / 2; 1162 final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter; 1163 final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter; 1164 final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0) 1165 : new TranslateAnimation(0, fromX, 0, fromY); 1166 set.addAnimation(scale); 1167 set.addAnimation(translation); 1168 1169 final IRemoteCallback callback = mAnimationFinishedCallback; 1170 if (callback != null) { 1171 set.setAnimationListener(new Animation.AnimationListener() { 1172 @Override 1173 public void onAnimationStart(Animation animation) { } 1174 1175 @Override 1176 public void onAnimationEnd(Animation animation) { 1177 mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget(); 1178 } 1179 1180 @Override 1181 public void onAnimationRepeat(Animation animation) { } 1182 }); 1183 } 1184 return set; 1185 } 1186 1187 /** 1188 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity 1189 * when a thumbnail is specified with the pending animation override. 1190 */ 1191 Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit, 1192 Bitmap thumbnailHeader) { 1193 Animation a; 1194 getDefaultNextAppTransitionStartRect(mTmpRect); 1195 final int thumbWidthI = thumbnailHeader.getWidth(); 1196 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 1197 final int thumbHeightI = thumbnailHeader.getHeight(); 1198 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 1199 1200 if (mNextAppTransitionScaleUp) { 1201 // Animation for the thumbnail zooming from its initial size to the full screen 1202 float scaleW = appWidth / thumbWidth; 1203 float scaleH = appHeight / thumbHeight; 1204 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 1205 computePivot(mTmpRect.left, 1 / scaleW), 1206 computePivot(mTmpRect.top, 1 / scaleH)); 1207 scale.setInterpolator(mDecelerateInterpolator); 1208 1209 Animation alpha = new AlphaAnimation(1, 0); 1210 alpha.setInterpolator(mThumbnailFadeOutInterpolator); 1211 1212 // This AnimationSet uses the Interpolators assigned above. 1213 AnimationSet set = new AnimationSet(false); 1214 set.addAnimation(scale); 1215 set.addAnimation(alpha); 1216 a = set; 1217 } else { 1218 // Animation for the thumbnail zooming down from the full screen to its final size 1219 float scaleW = appWidth / thumbWidth; 1220 float scaleH = appHeight / thumbHeight; 1221 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 1222 computePivot(mTmpRect.left, 1 / scaleW), 1223 computePivot(mTmpRect.top, 1 / scaleH)); 1224 } 1225 1226 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 1227 } 1228 1229 /** 1230 * This animation is created when we are doing a thumbnail transition, for the activity that is 1231 * leaving, and the activity that is entering. 1232 */ 1233 Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame, 1234 int transit, int taskId) { 1235 final int appWidth = containingFrame.width(); 1236 final int appHeight = containingFrame.height(); 1237 Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId); 1238 Animation a; 1239 getDefaultNextAppTransitionStartRect(mTmpRect); 1240 final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth; 1241 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 1242 final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight; 1243 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 1244 1245 switch (thumbTransitState) { 1246 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { 1247 // Entering app scales up with the thumbnail 1248 float scaleW = thumbWidth / appWidth; 1249 float scaleH = thumbHeight / appHeight; 1250 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 1251 computePivot(mTmpRect.left, scaleW), 1252 computePivot(mTmpRect.top, scaleH)); 1253 break; 1254 } 1255 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 1256 // Exiting app while the thumbnail is scaling up should fade or stay in place 1257 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1258 // Fade out while bringing up selected activity. This keeps the 1259 // current activity from showing through a launching wallpaper 1260 // activity. 1261 a = new AlphaAnimation(1, 0); 1262 } else { 1263 // noop animation 1264 a = new AlphaAnimation(1, 1); 1265 } 1266 break; 1267 } 1268 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 1269 // Entering the other app, it should just be visible while we scale the thumbnail 1270 // down above it 1271 a = new AlphaAnimation(1, 1); 1272 break; 1273 } 1274 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 1275 // Exiting the current app, the app should scale down with the thumbnail 1276 float scaleW = thumbWidth / appWidth; 1277 float scaleH = thumbHeight / appHeight; 1278 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 1279 computePivot(mTmpRect.left, scaleW), 1280 computePivot(mTmpRect.top, scaleH)); 1281 1282 Animation alpha = new AlphaAnimation(1, 0); 1283 1284 AnimationSet set = new AnimationSet(true); 1285 set.addAnimation(scale); 1286 set.addAnimation(alpha); 1287 set.setZAdjustment(Animation.ZORDER_TOP); 1288 a = set; 1289 break; 1290 } 1291 default: 1292 throw new RuntimeException("Invalid thumbnail transition state"); 1293 } 1294 1295 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 1296 } 1297 1298 private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) { 1299 getDefaultNextAppTransitionStartRect(mTmpFromClipRect); 1300 final int left = mTmpFromClipRect.left; 1301 final int top = mTmpFromClipRect.top; 1302 mTmpFromClipRect.offset(-left, -top); 1303 // TODO: Isn't that strange that we ignore exact position of the containingFrame? 1304 mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height()); 1305 AnimationSet set = new AnimationSet(true); 1306 float fromWidth = mTmpFromClipRect.width(); 1307 float toWidth = mTmpToClipRect.width(); 1308 float fromHeight = mTmpFromClipRect.height(); 1309 // While the window might span the whole display, the actual content will be cropped to the 1310 // system decoration frame, for example when the window is docked. We need to take into 1311 // account the visible height when constructing the animation. 1312 float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom; 1313 int translateAdjustment = 0; 1314 if (fromWidth <= toWidth && fromHeight <= toHeight) { 1315 // The final window is larger in both dimensions than current window (e.g. we are 1316 // maximizing), so we can simply unclip the new window and there will be no disappearing 1317 // frame. 1318 set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)); 1319 } else { 1320 // The disappearing window has one larger dimension. We need to apply scaling, so the 1321 // first frame of the entry animation matches the old window. 1322 set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1)); 1323 // We might not be going exactly full screen, but instead be aligned under the status 1324 // bar using cropping. We still need to account for the cropped part, which will also 1325 // be scaled. 1326 translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight); 1327 } 1328 1329 // We animate the translation from the old position of the removed window, to the new 1330 // position of the added window. The latter might not be full screen, for example docked for 1331 // docked windows. 1332 TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left, 1333 0, top - containingFrame.top - translateAdjustment, 0); 1334 set.addAnimation(translate); 1335 set.setDuration(DEFAULT_APP_TRANSITION_DURATION); 1336 set.setZAdjustment(Animation.ZORDER_TOP); 1337 return set; 1338 } 1339 1340 /** 1341 * @return true if and only if the first frame of the transition can be skipped, i.e. the first 1342 * frame of the transition doesn't change the visuals on screen, so we can start 1343 * directly with the second one 1344 */ 1345 boolean canSkipFirstFrame() { 1346 return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM 1347 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE 1348 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL; 1349 } 1350 1351 /** 1352 * 1353 * @param frame These are the bounds of the window when it finishes the animation. This is where 1354 * the animation must usually finish in entrance animation, as the next frame will 1355 * display the window at these coordinates. In case of exit animation, this is 1356 * where the animation must start, as the frame before the animation is displaying 1357 * the window at these bounds. 1358 * @param insets Knowing where the window will be positioned is not enough. Some parts of the 1359 * window might be obscured, usually by the system windows (status bar and 1360 * navigation bar) and we use content insets to convey that information. This 1361 * usually affects the animation aspects vertically, as the system decoration is 1362 * at the top and the bottom. For example when we animate from full screen to 1363 * recents, we want to exclude the covered parts, because they won't match the 1364 * thumbnail after the last frame is executed. 1365 * @param surfaceInsets In rare situation the surface is larger than the content and we need to 1366 * know about this to make the animation frames match. We currently use 1367 * this for freeform windows, which have larger surfaces to display 1368 * shadows. When we animate them from recents, we want to match the content 1369 * to the recents thumbnail and hence need to account for the surface being 1370 * bigger. 1371 */ 1372 Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, 1373 int orientation, Rect frame, Rect displayFrame, Rect insets, 1374 @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform, 1375 int taskId) { 1376 Animation a; 1377 if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN 1378 || transit == TRANSIT_TASK_OPEN 1379 || transit == TRANSIT_TASK_TO_FRONT)) { 1380 a = loadAnimationRes(lp, enter 1381 ? com.android.internal.R.anim.voice_activity_open_enter 1382 : com.android.internal.R.anim.voice_activity_open_exit); 1383 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1384 "applyAnimation voice:" 1385 + " anim=" + a + " transit=" + appTransitionToString(transit) 1386 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); 1387 } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE 1388 || transit == TRANSIT_TASK_CLOSE 1389 || transit == TRANSIT_TASK_TO_BACK)) { 1390 a = loadAnimationRes(lp, enter 1391 ? com.android.internal.R.anim.voice_activity_close_enter 1392 : com.android.internal.R.anim.voice_activity_close_exit); 1393 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1394 "applyAnimation voice:" 1395 + " anim=" + a + " transit=" + appTransitionToString(transit) 1396 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); 1397 } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) { 1398 a = createRelaunchAnimation(frame, insets); 1399 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1400 "applyAnimation:" 1401 + " anim=" + a + " nextAppTransition=" + mNextAppTransition 1402 + " transit=" + appTransitionToString(transit) 1403 + " Callers=" + Debug.getCallers(3)); 1404 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { 1405 a = loadAnimationRes(mNextAppTransitionPackage, enter ? 1406 mNextAppTransitionEnter : mNextAppTransitionExit); 1407 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1408 "applyAnimation:" 1409 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" 1410 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1411 + " Callers=" + Debug.getCallers(3)); 1412 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) { 1413 a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace); 1414 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1415 "applyAnimation:" 1416 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE" 1417 + " transit=" + appTransitionToString(transit) 1418 + " Callers=" + Debug.getCallers(3)); 1419 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) { 1420 a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame); 1421 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1422 "applyAnimation:" 1423 + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL" 1424 + " transit=" + appTransitionToString(transit) 1425 + " Callers=" + Debug.getCallers(3)); 1426 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { 1427 a = createScaleUpAnimationLocked(transit, enter, frame); 1428 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1429 "applyAnimation:" 1430 + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" 1431 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1432 + " Callers=" + Debug.getCallers(3)); 1433 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || 1434 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { 1435 mNextAppTransitionScaleUp = 1436 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); 1437 a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), 1438 frame, transit, taskId); 1439 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { 1440 String animName = mNextAppTransitionScaleUp ? 1441 "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; 1442 Slog.v(TAG, "applyAnimation:" 1443 + " anim=" + a + " nextAppTransition=" + animName 1444 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1445 + " Callers=" + Debug.getCallers(3)); 1446 } 1447 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || 1448 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) { 1449 mNextAppTransitionScaleUp = 1450 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); 1451 a = createAspectScaledThumbnailEnterExitAnimationLocked( 1452 getThumbnailTransitionState(enter), orientation, transit, frame, 1453 insets, surfaceInsets, freeform, taskId); 1454 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { 1455 String animName = mNextAppTransitionScaleUp ? 1456 "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN"; 1457 Slog.v(TAG, "applyAnimation:" 1458 + " anim=" + a + " nextAppTransition=" + animName 1459 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1460 + " Callers=" + Debug.getCallers(3)); 1461 } 1462 } else { 1463 int animAttr = 0; 1464 switch (transit) { 1465 case TRANSIT_ACTIVITY_OPEN: 1466 animAttr = enter 1467 ? WindowAnimation_activityOpenEnterAnimation 1468 : WindowAnimation_activityOpenExitAnimation; 1469 break; 1470 case TRANSIT_ACTIVITY_CLOSE: 1471 animAttr = enter 1472 ? WindowAnimation_activityCloseEnterAnimation 1473 : WindowAnimation_activityCloseExitAnimation; 1474 break; 1475 case TRANSIT_DOCK_TASK_FROM_RECENTS: 1476 case TRANSIT_TASK_OPEN: 1477 animAttr = enter 1478 ? WindowAnimation_taskOpenEnterAnimation 1479 : WindowAnimation_taskOpenExitAnimation; 1480 break; 1481 case TRANSIT_TASK_CLOSE: 1482 animAttr = enter 1483 ? WindowAnimation_taskCloseEnterAnimation 1484 : WindowAnimation_taskCloseExitAnimation; 1485 break; 1486 case TRANSIT_TASK_TO_FRONT: 1487 animAttr = enter 1488 ? WindowAnimation_taskToFrontEnterAnimation 1489 : WindowAnimation_taskToFrontExitAnimation; 1490 break; 1491 case TRANSIT_TASK_TO_BACK: 1492 animAttr = enter 1493 ? WindowAnimation_taskToBackEnterAnimation 1494 : WindowAnimation_taskToBackExitAnimation; 1495 break; 1496 case TRANSIT_WALLPAPER_OPEN: 1497 animAttr = enter 1498 ? WindowAnimation_wallpaperOpenEnterAnimation 1499 : WindowAnimation_wallpaperOpenExitAnimation; 1500 break; 1501 case TRANSIT_WALLPAPER_CLOSE: 1502 animAttr = enter 1503 ? WindowAnimation_wallpaperCloseEnterAnimation 1504 : WindowAnimation_wallpaperCloseExitAnimation; 1505 break; 1506 case TRANSIT_WALLPAPER_INTRA_OPEN: 1507 animAttr = enter 1508 ? WindowAnimation_wallpaperIntraOpenEnterAnimation 1509 : WindowAnimation_wallpaperIntraOpenExitAnimation; 1510 break; 1511 case TRANSIT_WALLPAPER_INTRA_CLOSE: 1512 animAttr = enter 1513 ? WindowAnimation_wallpaperIntraCloseEnterAnimation 1514 : WindowAnimation_wallpaperIntraCloseExitAnimation; 1515 break; 1516 case TRANSIT_TASK_OPEN_BEHIND: 1517 animAttr = enter 1518 ? WindowAnimation_launchTaskBehindSourceAnimation 1519 : WindowAnimation_launchTaskBehindTargetAnimation; 1520 } 1521 a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null; 1522 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1523 "applyAnimation:" 1524 + " anim=" + a 1525 + " animAttr=0x" + Integer.toHexString(animAttr) 1526 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1527 + " Callers=" + Debug.getCallers(3)); 1528 } 1529 return a; 1530 } 1531 1532 int getAppStackClipMode() { 1533 return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH 1534 ? STACK_CLIP_NONE 1535 : STACK_CLIP_AFTER_ANIM; 1536 } 1537 1538 void postAnimationCallback() { 1539 if (mNextAppTransitionCallback != null) { 1540 mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, 1541 mNextAppTransitionCallback)); 1542 mNextAppTransitionCallback = null; 1543 } 1544 } 1545 1546 void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, 1547 IRemoteCallback startedCallback) { 1548 if (isTransitionSet()) { 1549 clear(); 1550 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; 1551 mNextAppTransitionPackage = packageName; 1552 mNextAppTransitionEnter = enterAnim; 1553 mNextAppTransitionExit = exitAnim; 1554 postAnimationCallback(); 1555 mNextAppTransitionCallback = startedCallback; 1556 } else { 1557 postAnimationCallback(); 1558 } 1559 } 1560 1561 void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, 1562 int startHeight) { 1563 if (isTransitionSet()) { 1564 clear(); 1565 mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP; 1566 putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth, 1567 startY + startHeight, null); 1568 postAnimationCallback(); 1569 } 1570 } 1571 1572 void overridePendingAppTransitionClipReveal(int startX, int startY, 1573 int startWidth, int startHeight) { 1574 if (isTransitionSet()) { 1575 clear(); 1576 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL; 1577 putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null); 1578 postAnimationCallback(); 1579 } 1580 } 1581 1582 void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, 1583 IRemoteCallback startedCallback, boolean scaleUp) { 1584 if (isTransitionSet()) { 1585 clear(); 1586 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP 1587 : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN; 1588 mNextAppTransitionScaleUp = scaleUp; 1589 putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb); 1590 postAnimationCallback(); 1591 mNextAppTransitionCallback = startedCallback; 1592 } else { 1593 postAnimationCallback(); 1594 } 1595 } 1596 1597 void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY, 1598 int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) { 1599 if (isTransitionSet()) { 1600 clear(); 1601 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1602 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1603 mNextAppTransitionScaleUp = scaleUp; 1604 putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight, 1605 srcThumb); 1606 postAnimationCallback(); 1607 mNextAppTransitionCallback = startedCallback; 1608 } else { 1609 postAnimationCallback(); 1610 } 1611 } 1612 1613 public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, 1614 IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, 1615 boolean scaleUp) { 1616 if (isTransitionSet()) { 1617 clear(); 1618 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1619 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1620 mNextAppTransitionScaleUp = scaleUp; 1621 if (specs != null) { 1622 for (int i = 0; i < specs.length; i++) { 1623 AppTransitionAnimationSpec spec = specs[i]; 1624 if (spec != null) { 1625 mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec); 1626 if (i == 0) { 1627 // In full screen mode, the transition code depends on the default spec 1628 // to be set. 1629 Rect rect = spec.rect; 1630 putDefaultNextAppTransitionCoordinates(rect.left, rect.top, 1631 rect.width(), rect.height(), spec.bitmap); 1632 } 1633 } 1634 } 1635 } 1636 postAnimationCallback(); 1637 mNextAppTransitionCallback = onAnimationStartedCallback; 1638 mAnimationFinishedCallback = onAnimationFinishedCallback; 1639 } else { 1640 postAnimationCallback(); 1641 } 1642 } 1643 1644 void overridePendingAppTransitionMultiThumbFuture( 1645 IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, 1646 boolean scaleUp) { 1647 if (isTransitionSet()) { 1648 clear(); 1649 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1650 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1651 mNextAppTransitionAnimationsSpecsFuture = specsFuture; 1652 mNextAppTransitionScaleUp = scaleUp; 1653 mNextAppTransitionFutureCallback = callback; 1654 } 1655 } 1656 1657 void overrideInPlaceAppTransition(String packageName, int anim) { 1658 if (isTransitionSet()) { 1659 clear(); 1660 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE; 1661 mNextAppTransitionPackage = packageName; 1662 mNextAppTransitionInPlace = anim; 1663 } else { 1664 postAnimationCallback(); 1665 } 1666 } 1667 1668 /** 1669 * If a future is set for the app transition specs, fetch it in another thread. 1670 */ 1671 private void fetchAppTransitionSpecsFromFuture() { 1672 if (mNextAppTransitionAnimationsSpecsFuture != null) { 1673 mNextAppTransitionAnimationsSpecsPending = true; 1674 final IAppTransitionAnimationSpecsFuture future 1675 = mNextAppTransitionAnimationsSpecsFuture; 1676 mNextAppTransitionAnimationsSpecsFuture = null; 1677 mDefaultExecutor.execute(new Runnable() { 1678 @Override 1679 public void run() { 1680 AppTransitionAnimationSpec[] specs = null; 1681 try { 1682 specs = future.get(); 1683 } catch (RemoteException e) { 1684 Slog.w(TAG, "Failed to fetch app transition specs: " + e); 1685 } 1686 synchronized (mService.mWindowMap) { 1687 mNextAppTransitionAnimationsSpecsPending = false; 1688 overridePendingAppTransitionMultiThumb(specs, 1689 mNextAppTransitionFutureCallback, null /* finishedCallback */, 1690 mNextAppTransitionScaleUp); 1691 mNextAppTransitionFutureCallback = null; 1692 if (specs != null) { 1693 mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp); 1694 } 1695 } 1696 mService.requestTraversal(); 1697 } 1698 }); 1699 } 1700 } 1701 1702 @Override 1703 public String toString() { 1704 return "mNextAppTransition=" + appTransitionToString(mNextAppTransition); 1705 } 1706 1707 /** 1708 * Returns the human readable name of a window transition. 1709 * 1710 * @param transition The window transition. 1711 * @return The transition symbolic name. 1712 */ 1713 public static String appTransitionToString(int transition) { 1714 switch (transition) { 1715 case TRANSIT_UNSET: { 1716 return "TRANSIT_UNSET"; 1717 } 1718 case TRANSIT_NONE: { 1719 return "TRANSIT_NONE"; 1720 } 1721 case TRANSIT_ACTIVITY_OPEN: { 1722 return "TRANSIT_ACTIVITY_OPEN"; 1723 } 1724 case TRANSIT_ACTIVITY_CLOSE: { 1725 return "TRANSIT_ACTIVITY_CLOSE"; 1726 } 1727 case TRANSIT_TASK_OPEN: { 1728 return "TRANSIT_TASK_OPEN"; 1729 } 1730 case TRANSIT_TASK_CLOSE: { 1731 return "TRANSIT_TASK_CLOSE"; 1732 } 1733 case TRANSIT_TASK_TO_FRONT: { 1734 return "TRANSIT_TASK_TO_FRONT"; 1735 } 1736 case TRANSIT_TASK_TO_BACK: { 1737 return "TRANSIT_TASK_TO_BACK"; 1738 } 1739 case TRANSIT_WALLPAPER_CLOSE: { 1740 return "TRANSIT_WALLPAPER_CLOSE"; 1741 } 1742 case TRANSIT_WALLPAPER_OPEN: { 1743 return "TRANSIT_WALLPAPER_OPEN"; 1744 } 1745 case TRANSIT_WALLPAPER_INTRA_OPEN: { 1746 return "TRANSIT_WALLPAPER_INTRA_OPEN"; 1747 } 1748 case TRANSIT_WALLPAPER_INTRA_CLOSE: { 1749 return "TRANSIT_WALLPAPER_INTRA_CLOSE"; 1750 } 1751 case TRANSIT_TASK_OPEN_BEHIND: { 1752 return "TRANSIT_TASK_OPEN_BEHIND"; 1753 } 1754 case TRANSIT_ACTIVITY_RELAUNCH: { 1755 return "TRANSIT_ACTIVITY_RELAUNCH"; 1756 } 1757 case TRANSIT_DOCK_TASK_FROM_RECENTS: { 1758 return "TRANSIT_DOCK_TASK_FROM_RECENTS"; 1759 } 1760 default: { 1761 return "<UNKNOWN>"; 1762 } 1763 } 1764 } 1765 1766 private String appStateToString() { 1767 switch (mAppTransitionState) { 1768 case APP_STATE_IDLE: 1769 return "APP_STATE_IDLE"; 1770 case APP_STATE_READY: 1771 return "APP_STATE_READY"; 1772 case APP_STATE_RUNNING: 1773 return "APP_STATE_RUNNING"; 1774 case APP_STATE_TIMEOUT: 1775 return "APP_STATE_TIMEOUT"; 1776 default: 1777 return "unknown state=" + mAppTransitionState; 1778 } 1779 } 1780 1781 private String transitTypeToString() { 1782 switch (mNextAppTransitionType) { 1783 case NEXT_TRANSIT_TYPE_NONE: 1784 return "NEXT_TRANSIT_TYPE_NONE"; 1785 case NEXT_TRANSIT_TYPE_CUSTOM: 1786 return "NEXT_TRANSIT_TYPE_CUSTOM"; 1787 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE: 1788 return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE"; 1789 case NEXT_TRANSIT_TYPE_SCALE_UP: 1790 return "NEXT_TRANSIT_TYPE_SCALE_UP"; 1791 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 1792 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP"; 1793 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 1794 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN"; 1795 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: 1796 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP"; 1797 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: 1798 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN"; 1799 default: 1800 return "unknown type=" + mNextAppTransitionType; 1801 } 1802 } 1803 1804 @Override 1805 public void dump(PrintWriter pw, String prefix) { 1806 pw.print(prefix); pw.println(this); 1807 pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString()); 1808 if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) { 1809 pw.print(prefix); pw.print("mNextAppTransitionType="); 1810 pw.println(transitTypeToString()); 1811 } 1812 switch (mNextAppTransitionType) { 1813 case NEXT_TRANSIT_TYPE_CUSTOM: 1814 pw.print(prefix); pw.print("mNextAppTransitionPackage="); 1815 pw.println(mNextAppTransitionPackage); 1816 pw.print(prefix); pw.print("mNextAppTransitionEnter=0x"); 1817 pw.print(Integer.toHexString(mNextAppTransitionEnter)); 1818 pw.print(" mNextAppTransitionExit=0x"); 1819 pw.println(Integer.toHexString(mNextAppTransitionExit)); 1820 break; 1821 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE: 1822 pw.print(prefix); pw.print("mNextAppTransitionPackage="); 1823 pw.println(mNextAppTransitionPackage); 1824 pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x"); 1825 pw.print(Integer.toHexString(mNextAppTransitionInPlace)); 1826 break; 1827 case NEXT_TRANSIT_TYPE_SCALE_UP: { 1828 getDefaultNextAppTransitionStartRect(mTmpRect); 1829 pw.print(prefix); pw.print("mNextAppTransitionStartX="); 1830 pw.print(mTmpRect.left); 1831 pw.print(" mNextAppTransitionStartY="); 1832 pw.println(mTmpRect.top); 1833 pw.print(prefix); pw.print("mNextAppTransitionStartWidth="); 1834 pw.print(mTmpRect.width()); 1835 pw.print(" mNextAppTransitionStartHeight="); 1836 pw.println(mTmpRect.height()); 1837 break; 1838 } 1839 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 1840 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 1841 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: 1842 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: { 1843 pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec="); 1844 pw.println(mDefaultNextAppTransitionAnimationSpec); 1845 pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs="); 1846 pw.println(mNextAppTransitionAnimationsSpecs); 1847 pw.print(prefix); pw.print("mNextAppTransitionScaleUp="); 1848 pw.println(mNextAppTransitionScaleUp); 1849 break; 1850 } 1851 } 1852 if (mNextAppTransitionCallback != null) { 1853 pw.print(prefix); pw.print("mNextAppTransitionCallback="); 1854 pw.println(mNextAppTransitionCallback); 1855 } 1856 } 1857 1858 public void setCurrentUser(int newUserId) { 1859 mCurrentUserId = newUserId; 1860 } 1861 1862 /** 1863 * @return true if transition is not running and should not be skipped, false if transition is 1864 * already running 1865 */ 1866 boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) { 1867 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:" 1868 + " transit=" + appTransitionToString(transit) 1869 + " " + this 1870 + " alwaysKeepCurrent=" + alwaysKeepCurrent 1871 + " Callers=" + Debug.getCallers(3)); 1872 if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) { 1873 setAppTransition(transit); 1874 } else if (!alwaysKeepCurrent) { 1875 if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) { 1876 // Opening a new task always supersedes a close for the anim. 1877 setAppTransition(transit); 1878 } else if (transit == TRANSIT_ACTIVITY_OPEN 1879 && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) { 1880 // Opening a new activity always supersedes a close for the anim. 1881 setAppTransition(transit); 1882 } 1883 } 1884 boolean prepared = prepare(); 1885 if (isTransitionSet()) { 1886 mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); 1887 mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); 1888 } 1889 return prepared; 1890 } 1891} 1892