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