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