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