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