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