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