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.os.SystemProperties; 61 import android.util.ArraySet; 62 import android.util.Slog; 63 import android.util.SparseArray; 64 import android.view.AppTransitionAnimationSpec; 65 import android.view.IAppTransitionAnimationSpecsFuture; 66 import android.view.WindowManager; 67 import android.view.animation.AlphaAnimation; 68 import android.view.animation.Animation; 69 import android.view.animation.AnimationSet; 70 import android.view.animation.AnimationUtils; 71 import android.view.animation.ClipRectAnimation; 72 import android.view.animation.Interpolator; 73 import android.view.animation.PathInterpolator; 74 import android.view.animation.ScaleAnimation; 75 import android.view.animation.TranslateAnimation; 76 77 import com.android.internal.util.DumpUtils.Dump; 78 import com.android.server.AttributeCache; 79 import com.android.server.wm.WindowManagerService.H; 80 import com.android.server.wm.animation.ClipRectLRAnimation; 81 import com.android.server.wm.animation.ClipRectTBAnimation; 82 import com.android.server.wm.animation.CurvedTranslateAnimation; 83 84 import java.io.PrintWriter; 85 import java.util.ArrayList; 86 import java.util.concurrent.ExecutorService; 87 import 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. 94 public 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 AppTransition(Context context, WindowManagerService service)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 isTransitionSet()281 boolean isTransitionSet() { 282 return mNextAppTransition != TRANSIT_UNSET; 283 } 284 isTransitionEqual(int transit)285 boolean isTransitionEqual(int transit) { 286 return mNextAppTransition == transit; 287 } 288 getAppTransition()289 int getAppTransition() { 290 return mNextAppTransition; 291 } 292 setAppTransition(int transit)293 private void setAppTransition(int transit) { 294 mNextAppTransition = transit; 295 setLastAppTransition(TRANSIT_UNSET, null, null); 296 } 297 setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp)298 void setLastAppTransition(int transit, AppWindowToken openingApp, AppWindowToken closingApp) { 299 mLastUsedAppTransition = transit; 300 mLastOpeningApp = "" + openingApp; 301 mLastClosingApp = "" + closingApp; 302 } 303 isReady()304 boolean isReady() { 305 return mAppTransitionState == APP_STATE_READY 306 || mAppTransitionState == APP_STATE_TIMEOUT; 307 } 308 setReady()309 void setReady() { 310 mAppTransitionState = APP_STATE_READY; 311 fetchAppTransitionSpecsFromFuture(); 312 } 313 isRunning()314 boolean isRunning() { 315 return mAppTransitionState == APP_STATE_RUNNING; 316 } 317 setIdle()318 void setIdle() { 319 mAppTransitionState = APP_STATE_IDLE; 320 } 321 isTimeout()322 boolean isTimeout() { 323 return mAppTransitionState == APP_STATE_TIMEOUT; 324 } 325 setTimeout()326 void setTimeout() { 327 mAppTransitionState = APP_STATE_TIMEOUT; 328 } 329 getAppTransitionThumbnailHeader(int taskId)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. */ isNextThumbnailTransitionAspectScaled()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. */ isNextThumbnailTransitionScaleUp()345 boolean isNextThumbnailTransitionScaleUp() { 346 return mNextAppTransitionScaleUp; 347 } 348 isNextAppTransitionThumbnailUp()349 boolean isNextAppTransitionThumbnailUp() { 350 return mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || 351 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP; 352 } 353 isNextAppTransitionThumbnailDown()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 */ isFetchingAppTransitionsSpecs()363 boolean isFetchingAppTransitionsSpecs() { 364 return mNextAppTransitionAnimationsSpecsPending; 365 } 366 prepare()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 goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator, ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps)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 */ notifyProlongedAnimationsEnded()405 void notifyProlongedAnimationsEnded() { 406 mProlongedAnimationsEnded = true; 407 } 408 clear()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 freeze()419 void freeze() { 420 setAppTransition(AppTransition.TRANSIT_UNSET); 421 clear(); 422 setReady(); 423 notifyAppTransitionCancelledLocked(); 424 } 425 registerListenerLocked(AppTransitionListener listener)426 void registerListenerLocked(AppTransitionListener listener) { 427 mListeners.add(listener); 428 } 429 notifyAppTransitionFinishedLocked(IBinder token)430 public void notifyAppTransitionFinishedLocked(IBinder token) { 431 for (int i = 0; i < mListeners.size(); i++) { 432 mListeners.get(i).onAppTransitionFinishedLocked(token); 433 } 434 } 435 notifyAppTransitionPendingLocked()436 private void notifyAppTransitionPendingLocked() { 437 for (int i = 0; i < mListeners.size(); i++) { 438 mListeners.get(i).onAppTransitionPendingLocked(); 439 } 440 } 441 notifyAppTransitionCancelledLocked()442 private void notifyAppTransitionCancelledLocked() { 443 for (int i = 0; i < mListeners.size(); i++) { 444 mListeners.get(i).onAppTransitionCancelledLocked(); 445 } 446 } 447 notifyAppTransitionStartingLocked(IBinder openToken, IBinder closeToken, Animation openAnimation, Animation closeAnimation)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 getCachedAnimations(WindowManager.LayoutParams lp)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 getCachedAnimations(String packageName, int resId)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 loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr)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 loadAnimationRes(WindowManager.LayoutParams lp, int resId)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 loadAnimationRes(String packageName, int resId)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 */ computePivot(int startPos, float finalScale)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 createScaleUpAnimationLocked(int transit, boolean enter, Rect containingFrame)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 getDefaultNextAppTransitionStartRect(Rect rect)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 getNextAppTransitionStartRect(int taskId, Rect rect)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 putDefaultNextAppTransitionCoordinates(int left, int top, int width, int height, Bitmap bitmap)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 */ getLastClipRevealTransitionDuration()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 */ getLastClipRevealMaxTranslation()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 */ hadClipRevealAnimation()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 */ calculateClipRevealTransitionDuration(boolean cutOff, float translationX, float translationY, Rect displayFrame)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 createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame, Rect displayFrame)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 */ prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight, long duration, Interpolator interpolator)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 */ prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit)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 */ getThumbnailTransitionState(boolean enter)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 */ createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets, Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation)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 float fromY; 929 final float toX; 930 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 if (mGridLayoutRecentsEnabled) { 944 // In the grid layout, the header is displayed above the thumbnail instead of 945 // overlapping it. 946 fromY -= thumbHeightI; 947 toY -= thumbHeightI * scaleW; 948 } 949 } else { 950 pivotX = 0; 951 pivotY = 0; 952 fromX = mTmpRect.left; 953 fromY = mTmpRect.top; 954 toX = appRect.left; 955 toY = appRect.top; 956 } 957 final long duration = getAspectScaleDuration(); 958 final Interpolator interpolator = getAspectScaleInterpolator(); 959 if (mNextAppTransitionScaleUp) { 960 // Animation up from the thumbnail to the full screen 961 Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY); 962 scale.setInterpolator(interpolator); 963 scale.setDuration(duration); 964 Animation alpha = new AlphaAnimation(1f, 0f); 965 alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS 966 ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator); 967 alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS 968 ? duration / 2 969 : duration); 970 Animation translate = createCurvedMotion(fromX, toX, fromY, toY); 971 translate.setInterpolator(interpolator); 972 translate.setDuration(duration); 973 974 mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI); 975 mTmpToClipRect.set(appRect); 976 977 // Containing frame is in screen space, but we need the clip rect in the 978 // app space. 979 mTmpToClipRect.offsetTo(0, 0); 980 mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW); 981 mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW); 982 983 if (contentInsets != null) { 984 mTmpToClipRect.inset((int) (-contentInsets.left * scaleW), 985 (int) (-contentInsets.top * scaleW), 986 (int) (-contentInsets.right * scaleW), 987 (int) (-contentInsets.bottom * scaleW)); 988 } 989 990 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 991 clipAnim.setInterpolator(interpolator); 992 clipAnim.setDuration(duration); 993 994 // This AnimationSet uses the Interpolators assigned above. 995 AnimationSet set = new AnimationSet(false); 996 set.addAnimation(scale); 997 if (!mGridLayoutRecentsEnabled) { 998 // In the grid layout, the header should be shown for the whole animation. 999 set.addAnimation(alpha); 1000 } 1001 set.addAnimation(translate); 1002 set.addAnimation(clipAnim); 1003 a = set; 1004 } else { 1005 // Animation down from the full screen to the thumbnail 1006 Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY); 1007 scale.setInterpolator(interpolator); 1008 scale.setDuration(duration); 1009 Animation alpha = new AlphaAnimation(0f, 1f); 1010 alpha.setInterpolator(mThumbnailFadeInInterpolator); 1011 alpha.setDuration(duration); 1012 Animation translate = createCurvedMotion(toX, fromX, toY, fromY); 1013 translate.setInterpolator(interpolator); 1014 translate.setDuration(duration); 1015 1016 // This AnimationSet uses the Interpolators assigned above. 1017 AnimationSet set = new AnimationSet(false); 1018 set.addAnimation(scale); 1019 if (!mGridLayoutRecentsEnabled) { 1020 // In the grid layout, the header should be shown for the whole animation. 1021 set.addAnimation(alpha); 1022 } 1023 set.addAnimation(translate); 1024 a = set; 1025 1026 } 1027 return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0, 1028 null); 1029 } 1030 createCurvedMotion(float fromX, float toX, float fromY, float toY)1031 private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) { 1032 1033 // Almost no x-change - use linear animation 1034 if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) { 1035 return new TranslateAnimation(fromX, toX, fromY, toY); 1036 } else { 1037 final Path path = createCurvedPath(fromX, toX, fromY, toY); 1038 return new CurvedTranslateAnimation(path); 1039 } 1040 } 1041 createCurvedPath(float fromX, float toX, float fromY, float toY)1042 private Path createCurvedPath(float fromX, float toX, float fromY, float toY) { 1043 final Path path = new Path(); 1044 path.moveTo(fromX, fromY); 1045 1046 if (fromY > toY) { 1047 // If the object needs to go up, move it in horizontal direction first, then vertical. 1048 path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY); 1049 } else { 1050 // If the object needs to go down, move it in vertical direction first, then horizontal. 1051 path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY); 1052 } 1053 return path; 1054 } 1055 getAspectScaleDuration()1056 private long getAspectScaleDuration() { 1057 if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) { 1058 return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f); 1059 } else { 1060 return THUMBNAIL_APP_TRANSITION_DURATION; 1061 } 1062 } 1063 getAspectScaleInterpolator()1064 private Interpolator getAspectScaleInterpolator() { 1065 if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) { 1066 return mFastOutSlowInInterpolator; 1067 } else { 1068 return TOUCH_RESPONSE_INTERPOLATOR; 1069 } 1070 } 1071 1072 /** 1073 * This alternate animation is created when we are doing a thumbnail transition, for the 1074 * activity that is leaving, and the activity that is entering. 1075 */ createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets, @Nullable Rect surfaceInsets, boolean freeform, int taskId)1076 Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, 1077 int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets, 1078 @Nullable Rect surfaceInsets, boolean freeform, int taskId) { 1079 Animation a; 1080 final int appWidth = containingFrame.width(); 1081 final int appHeight = containingFrame.height(); 1082 getDefaultNextAppTransitionStartRect(mTmpRect); 1083 final int thumbWidthI = mTmpRect.width(); 1084 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 1085 final int thumbHeightI = mTmpRect.height(); 1086 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 1087 final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left; 1088 final int thumbStartY = mTmpRect.top - containingFrame.top; 1089 1090 switch (thumbTransitState) { 1091 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: 1092 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 1093 final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP; 1094 if (freeform && scaleUp) { 1095 a = createAspectScaledThumbnailEnterFreeformAnimationLocked( 1096 containingFrame, surfaceInsets, taskId); 1097 } else if (freeform) { 1098 a = createAspectScaledThumbnailExitFreeformAnimationLocked( 1099 containingFrame, surfaceInsets, taskId); 1100 } else { 1101 AnimationSet set = new AnimationSet(true); 1102 1103 // In portrait, we scale to fit the width 1104 mTmpFromClipRect.set(containingFrame); 1105 mTmpToClipRect.set(containingFrame); 1106 1107 // Containing frame is in screen space, but we need the clip rect in the 1108 // app space. 1109 mTmpFromClipRect.offsetTo(0, 0); 1110 mTmpToClipRect.offsetTo(0, 0); 1111 1112 // Exclude insets region from the source clip. 1113 mTmpFromClipRect.inset(contentInsets); 1114 mNextAppTransitionInsets.set(contentInsets); 1115 1116 if (shouldScaleDownThumbnailTransition(uiMode, orientation)) { 1117 // We scale the width and clip to the top/left square 1118 float scale = thumbWidth / 1119 (appWidth - contentInsets.left - contentInsets.right); 1120 if (!mGridLayoutRecentsEnabled) { 1121 int unscaledThumbHeight = (int) (thumbHeight / scale); 1122 mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight; 1123 } 1124 1125 mNextAppTransitionInsets.set(contentInsets); 1126 1127 Animation scaleAnim = new ScaleAnimation( 1128 scaleUp ? scale : 1, scaleUp ? 1 : scale, 1129 scaleUp ? scale : 1, scaleUp ? 1 : scale, 1130 containingFrame.width() / 2f, 1131 containingFrame.height() / 2f + contentInsets.top); 1132 final float targetX = (mTmpRect.left - containingFrame.left); 1133 final float x = containingFrame.width() / 2f 1134 - containingFrame.width() / 2f * scale; 1135 final float targetY = (mTmpRect.top - containingFrame.top); 1136 final float y = containingFrame.height() / 2f 1137 - containingFrame.height() / 2f * scale; 1138 final float startX = targetX - x; 1139 final float startY = targetY - y; 1140 Animation clipAnim = scaleUp 1141 ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect) 1142 : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect); 1143 Animation translateAnim = scaleUp 1144 ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0) 1145 : createCurvedMotion(0, startX, 0, startY - contentInsets.top); 1146 1147 set.addAnimation(clipAnim); 1148 set.addAnimation(scaleAnim); 1149 set.addAnimation(translateAnim); 1150 1151 } else { 1152 // In landscape, we don't scale at all and only crop 1153 mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI; 1154 mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI; 1155 1156 Animation clipAnim = scaleUp 1157 ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect) 1158 : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect); 1159 Animation translateAnim = scaleUp 1160 ? createCurvedMotion(thumbStartX, 0, 1161 thumbStartY - contentInsets.top, 0) 1162 : createCurvedMotion(0, thumbStartX, 0, 1163 thumbStartY - contentInsets.top); 1164 1165 set.addAnimation(clipAnim); 1166 set.addAnimation(translateAnim); 1167 } 1168 a = set; 1169 a.setZAdjustment(Animation.ZORDER_TOP); 1170 } 1171 break; 1172 } 1173 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 1174 // Previous app window during the scale up 1175 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1176 // Fade out the source activity if we are animating to a wallpaper 1177 // activity. 1178 a = new AlphaAnimation(1, 0); 1179 } else { 1180 a = new AlphaAnimation(1, 1); 1181 } 1182 break; 1183 } 1184 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 1185 // Target app window during the scale down 1186 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1187 // Fade in the destination activity if we are animating from a wallpaper 1188 // activity. 1189 a = new AlphaAnimation(0, 1); 1190 } else { 1191 a = new AlphaAnimation(1, 1); 1192 } 1193 break; 1194 } 1195 default: 1196 throw new RuntimeException("Invalid thumbnail transition state"); 1197 } 1198 1199 return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, 1200 getAspectScaleDuration(), getAspectScaleInterpolator()); 1201 } 1202 createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame, @Nullable Rect surfaceInsets, int taskId)1203 private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame, 1204 @Nullable Rect surfaceInsets, int taskId) { 1205 getNextAppTransitionStartRect(taskId, mTmpRect); 1206 return createAspectScaledThumbnailFreeformAnimationLocked(mTmpRect, frame, surfaceInsets, 1207 true); 1208 } 1209 createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame, @Nullable Rect surfaceInsets, int taskId)1210 private Animation createAspectScaledThumbnailExitFreeformAnimationLocked(Rect frame, 1211 @Nullable Rect surfaceInsets, int taskId) { 1212 getNextAppTransitionStartRect(taskId, mTmpRect); 1213 return createAspectScaledThumbnailFreeformAnimationLocked(frame, mTmpRect, surfaceInsets, 1214 false); 1215 } 1216 createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame, Rect destFrame, @Nullable Rect surfaceInsets, boolean enter)1217 private AnimationSet createAspectScaledThumbnailFreeformAnimationLocked(Rect sourceFrame, 1218 Rect destFrame, @Nullable Rect surfaceInsets, boolean enter) { 1219 final float sourceWidth = sourceFrame.width(); 1220 final float sourceHeight = sourceFrame.height(); 1221 final float destWidth = destFrame.width(); 1222 final float destHeight = destFrame.height(); 1223 final float scaleH = enter ? sourceWidth / destWidth : destWidth / sourceWidth; 1224 final float scaleV = enter ? sourceHeight / destHeight : destHeight / sourceHeight; 1225 AnimationSet set = new AnimationSet(true); 1226 final int surfaceInsetsH = surfaceInsets == null 1227 ? 0 : surfaceInsets.left + surfaceInsets.right; 1228 final int surfaceInsetsV = surfaceInsets == null 1229 ? 0 : surfaceInsets.top + surfaceInsets.bottom; 1230 // We want the scaling to happen from the center of the surface. In order to achieve that, 1231 // we need to account for surface insets that will be used to enlarge the surface. 1232 final float scaleHCenter = ((enter ? destWidth : sourceWidth) + surfaceInsetsH) / 2; 1233 final float scaleVCenter = ((enter ? destHeight : sourceHeight) + surfaceInsetsV) / 2; 1234 final ScaleAnimation scale = enter ? 1235 new ScaleAnimation(scaleH, 1, scaleV, 1, scaleHCenter, scaleVCenter) 1236 : new ScaleAnimation(1, scaleH, 1, scaleV, scaleHCenter, scaleVCenter); 1237 final int sourceHCenter = sourceFrame.left + sourceFrame.width() / 2; 1238 final int sourceVCenter = sourceFrame.top + sourceFrame.height() / 2; 1239 final int destHCenter = destFrame.left + destFrame.width() / 2; 1240 final int destVCenter = destFrame.top + destFrame.height() / 2; 1241 final int fromX = enter ? sourceHCenter - destHCenter : destHCenter - sourceHCenter; 1242 final int fromY = enter ? sourceVCenter - destVCenter : destVCenter - sourceVCenter; 1243 final TranslateAnimation translation = enter ? new TranslateAnimation(fromX, 0, fromY, 0) 1244 : new TranslateAnimation(0, fromX, 0, fromY); 1245 set.addAnimation(scale); 1246 set.addAnimation(translation); 1247 1248 final IRemoteCallback callback = mAnimationFinishedCallback; 1249 if (callback != null) { 1250 set.setAnimationListener(new Animation.AnimationListener() { 1251 @Override 1252 public void onAnimationStart(Animation animation) { } 1253 1254 @Override 1255 public void onAnimationEnd(Animation animation) { 1256 mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget(); 1257 } 1258 1259 @Override 1260 public void onAnimationRepeat(Animation animation) { } 1261 }); 1262 } 1263 return set; 1264 } 1265 1266 /** 1267 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity 1268 * when a thumbnail is specified with the pending animation override. 1269 */ createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit, Bitmap thumbnailHeader)1270 Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit, 1271 Bitmap thumbnailHeader) { 1272 Animation a; 1273 getDefaultNextAppTransitionStartRect(mTmpRect); 1274 final int thumbWidthI = thumbnailHeader.getWidth(); 1275 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 1276 final int thumbHeightI = thumbnailHeader.getHeight(); 1277 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 1278 1279 if (mNextAppTransitionScaleUp) { 1280 // Animation for the thumbnail zooming from its initial size to the full screen 1281 float scaleW = appWidth / thumbWidth; 1282 float scaleH = appHeight / thumbHeight; 1283 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 1284 computePivot(mTmpRect.left, 1 / scaleW), 1285 computePivot(mTmpRect.top, 1 / scaleH)); 1286 scale.setInterpolator(mDecelerateInterpolator); 1287 1288 Animation alpha = new AlphaAnimation(1, 0); 1289 alpha.setInterpolator(mThumbnailFadeOutInterpolator); 1290 1291 // This AnimationSet uses the Interpolators assigned above. 1292 AnimationSet set = new AnimationSet(false); 1293 set.addAnimation(scale); 1294 set.addAnimation(alpha); 1295 a = set; 1296 } else { 1297 // Animation for the thumbnail zooming down from the full screen to its final size 1298 float scaleW = appWidth / thumbWidth; 1299 float scaleH = appHeight / thumbHeight; 1300 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 1301 computePivot(mTmpRect.left, 1 / scaleW), 1302 computePivot(mTmpRect.top, 1 / scaleH)); 1303 } 1304 1305 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 1306 } 1307 1308 /** 1309 * This animation is created when we are doing a thumbnail transition, for the activity that is 1310 * leaving, and the activity that is entering. 1311 */ createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame, int transit, int taskId)1312 Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame, 1313 int transit, int taskId) { 1314 final int appWidth = containingFrame.width(); 1315 final int appHeight = containingFrame.height(); 1316 Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId); 1317 Animation a; 1318 getDefaultNextAppTransitionStartRect(mTmpRect); 1319 final int thumbWidthI = thumbnailHeader != null ? thumbnailHeader.getWidth() : appWidth; 1320 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 1321 final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight; 1322 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 1323 1324 switch (thumbTransitState) { 1325 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { 1326 // Entering app scales up with the thumbnail 1327 float scaleW = thumbWidth / appWidth; 1328 float scaleH = thumbHeight / appHeight; 1329 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 1330 computePivot(mTmpRect.left, scaleW), 1331 computePivot(mTmpRect.top, scaleH)); 1332 break; 1333 } 1334 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 1335 // Exiting app while the thumbnail is scaling up should fade or stay in place 1336 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 1337 // Fade out while bringing up selected activity. This keeps the 1338 // current activity from showing through a launching wallpaper 1339 // activity. 1340 a = new AlphaAnimation(1, 0); 1341 } else { 1342 // noop animation 1343 a = new AlphaAnimation(1, 1); 1344 } 1345 break; 1346 } 1347 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 1348 // Entering the other app, it should just be visible while we scale the thumbnail 1349 // down above it 1350 a = new AlphaAnimation(1, 1); 1351 break; 1352 } 1353 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 1354 // Exiting the current app, the app should scale down with the thumbnail 1355 float scaleW = thumbWidth / appWidth; 1356 float scaleH = thumbHeight / appHeight; 1357 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 1358 computePivot(mTmpRect.left, scaleW), 1359 computePivot(mTmpRect.top, scaleH)); 1360 1361 Animation alpha = new AlphaAnimation(1, 0); 1362 1363 AnimationSet set = new AnimationSet(true); 1364 set.addAnimation(scale); 1365 set.addAnimation(alpha); 1366 set.setZAdjustment(Animation.ZORDER_TOP); 1367 a = set; 1368 break; 1369 } 1370 default: 1371 throw new RuntimeException("Invalid thumbnail transition state"); 1372 } 1373 1374 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 1375 } 1376 createRelaunchAnimation(Rect containingFrame, Rect contentInsets)1377 private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) { 1378 getDefaultNextAppTransitionStartRect(mTmpFromClipRect); 1379 final int left = mTmpFromClipRect.left; 1380 final int top = mTmpFromClipRect.top; 1381 mTmpFromClipRect.offset(-left, -top); 1382 // TODO: Isn't that strange that we ignore exact position of the containingFrame? 1383 mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height()); 1384 AnimationSet set = new AnimationSet(true); 1385 float fromWidth = mTmpFromClipRect.width(); 1386 float toWidth = mTmpToClipRect.width(); 1387 float fromHeight = mTmpFromClipRect.height(); 1388 // While the window might span the whole display, the actual content will be cropped to the 1389 // system decoration frame, for example when the window is docked. We need to take into 1390 // account the visible height when constructing the animation. 1391 float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom; 1392 int translateAdjustment = 0; 1393 if (fromWidth <= toWidth && fromHeight <= toHeight) { 1394 // The final window is larger in both dimensions than current window (e.g. we are 1395 // maximizing), so we can simply unclip the new window and there will be no disappearing 1396 // frame. 1397 set.addAnimation(new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)); 1398 } else { 1399 // The disappearing window has one larger dimension. We need to apply scaling, so the 1400 // first frame of the entry animation matches the old window. 1401 set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1)); 1402 // We might not be going exactly full screen, but instead be aligned under the status 1403 // bar using cropping. We still need to account for the cropped part, which will also 1404 // be scaled. 1405 translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight); 1406 } 1407 1408 // We animate the translation from the old position of the removed window, to the new 1409 // position of the added window. The latter might not be full screen, for example docked for 1410 // docked windows. 1411 TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left, 1412 0, top - containingFrame.top - translateAdjustment, 0); 1413 set.addAnimation(translate); 1414 set.setDuration(DEFAULT_APP_TRANSITION_DURATION); 1415 set.setZAdjustment(Animation.ZORDER_TOP); 1416 return set; 1417 } 1418 1419 /** 1420 * @return true if and only if the first frame of the transition can be skipped, i.e. the first 1421 * frame of the transition doesn't change the visuals on screen, so we can start 1422 * directly with the second one 1423 */ canSkipFirstFrame()1424 boolean canSkipFirstFrame() { 1425 return mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM 1426 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE 1427 && mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL; 1428 } 1429 1430 /** 1431 * 1432 * @param frame These are the bounds of the window when it finishes the animation. This is where 1433 * the animation must usually finish in entrance animation, as the next frame will 1434 * display the window at these coordinates. In case of exit animation, this is 1435 * where the animation must start, as the frame before the animation is displaying 1436 * the window at these bounds. 1437 * @param insets Knowing where the window will be positioned is not enough. Some parts of the 1438 * window might be obscured, usually by the system windows (status bar and 1439 * navigation bar) and we use content insets to convey that information. This 1440 * usually affects the animation aspects vertically, as the system decoration is 1441 * at the top and the bottom. For example when we animate from full screen to 1442 * recents, we want to exclude the covered parts, because they won't match the 1443 * thumbnail after the last frame is executed. 1444 * @param surfaceInsets In rare situation the surface is larger than the content and we need to 1445 * know about this to make the animation frames match. We currently use 1446 * this for freeform windows, which have larger surfaces to display 1447 * shadows. When we animate them from recents, we want to match the content 1448 * to the recents thumbnail and hence need to account for the surface being 1449 * bigger. 1450 */ loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode, int orientation, Rect frame, Rect displayFrame, Rect insets, @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform, int taskId)1451 Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode, 1452 int orientation, Rect frame, Rect displayFrame, Rect insets, 1453 @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform, 1454 int taskId) { 1455 Animation a; 1456 if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN 1457 || transit == TRANSIT_TASK_OPEN 1458 || transit == TRANSIT_TASK_TO_FRONT)) { 1459 a = loadAnimationRes(lp, enter 1460 ? com.android.internal.R.anim.voice_activity_open_enter 1461 : com.android.internal.R.anim.voice_activity_open_exit); 1462 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1463 "applyAnimation voice:" 1464 + " anim=" + a + " transit=" + appTransitionToString(transit) 1465 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); 1466 } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE 1467 || transit == TRANSIT_TASK_CLOSE 1468 || transit == TRANSIT_TASK_TO_BACK)) { 1469 a = loadAnimationRes(lp, enter 1470 ? com.android.internal.R.anim.voice_activity_close_enter 1471 : com.android.internal.R.anim.voice_activity_close_exit); 1472 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1473 "applyAnimation voice:" 1474 + " anim=" + a + " transit=" + appTransitionToString(transit) 1475 + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); 1476 } else if (transit == TRANSIT_ACTIVITY_RELAUNCH) { 1477 a = createRelaunchAnimation(frame, insets); 1478 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1479 "applyAnimation:" 1480 + " anim=" + a + " nextAppTransition=" + mNextAppTransition 1481 + " transit=" + appTransitionToString(transit) 1482 + " Callers=" + Debug.getCallers(3)); 1483 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { 1484 a = loadAnimationRes(mNextAppTransitionPackage, enter ? 1485 mNextAppTransitionEnter : mNextAppTransitionExit); 1486 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1487 "applyAnimation:" 1488 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" 1489 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1490 + " Callers=" + Debug.getCallers(3)); 1491 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) { 1492 a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace); 1493 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1494 "applyAnimation:" 1495 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE" 1496 + " transit=" + appTransitionToString(transit) 1497 + " Callers=" + Debug.getCallers(3)); 1498 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) { 1499 a = createClipRevealAnimationLocked(transit, enter, frame, displayFrame); 1500 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1501 "applyAnimation:" 1502 + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL" 1503 + " transit=" + appTransitionToString(transit) 1504 + " Callers=" + Debug.getCallers(3)); 1505 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { 1506 a = createScaleUpAnimationLocked(transit, enter, frame); 1507 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1508 "applyAnimation:" 1509 + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" 1510 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1511 + " Callers=" + Debug.getCallers(3)); 1512 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || 1513 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { 1514 mNextAppTransitionScaleUp = 1515 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); 1516 a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), 1517 frame, transit, taskId); 1518 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { 1519 String animName = mNextAppTransitionScaleUp ? 1520 "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; 1521 Slog.v(TAG, "applyAnimation:" 1522 + " anim=" + a + " nextAppTransition=" + animName 1523 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1524 + " Callers=" + Debug.getCallers(3)); 1525 } 1526 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || 1527 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) { 1528 mNextAppTransitionScaleUp = 1529 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); 1530 a = createAspectScaledThumbnailEnterExitAnimationLocked( 1531 getThumbnailTransitionState(enter), uiMode, orientation, transit, frame, 1532 insets, surfaceInsets, freeform, taskId); 1533 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { 1534 String animName = mNextAppTransitionScaleUp ? 1535 "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN"; 1536 Slog.v(TAG, "applyAnimation:" 1537 + " anim=" + a + " nextAppTransition=" + animName 1538 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1539 + " Callers=" + Debug.getCallers(3)); 1540 } 1541 } else { 1542 int animAttr = 0; 1543 switch (transit) { 1544 case TRANSIT_ACTIVITY_OPEN: 1545 animAttr = enter 1546 ? WindowAnimation_activityOpenEnterAnimation 1547 : WindowAnimation_activityOpenExitAnimation; 1548 break; 1549 case TRANSIT_ACTIVITY_CLOSE: 1550 animAttr = enter 1551 ? WindowAnimation_activityCloseEnterAnimation 1552 : WindowAnimation_activityCloseExitAnimation; 1553 break; 1554 case TRANSIT_DOCK_TASK_FROM_RECENTS: 1555 case TRANSIT_TASK_OPEN: 1556 animAttr = enter 1557 ? WindowAnimation_taskOpenEnterAnimation 1558 : WindowAnimation_taskOpenExitAnimation; 1559 break; 1560 case TRANSIT_TASK_CLOSE: 1561 animAttr = enter 1562 ? WindowAnimation_taskCloseEnterAnimation 1563 : WindowAnimation_taskCloseExitAnimation; 1564 break; 1565 case TRANSIT_TASK_TO_FRONT: 1566 animAttr = enter 1567 ? WindowAnimation_taskToFrontEnterAnimation 1568 : WindowAnimation_taskToFrontExitAnimation; 1569 break; 1570 case TRANSIT_TASK_TO_BACK: 1571 animAttr = enter 1572 ? WindowAnimation_taskToBackEnterAnimation 1573 : WindowAnimation_taskToBackExitAnimation; 1574 break; 1575 case TRANSIT_WALLPAPER_OPEN: 1576 animAttr = enter 1577 ? WindowAnimation_wallpaperOpenEnterAnimation 1578 : WindowAnimation_wallpaperOpenExitAnimation; 1579 break; 1580 case TRANSIT_WALLPAPER_CLOSE: 1581 animAttr = enter 1582 ? WindowAnimation_wallpaperCloseEnterAnimation 1583 : WindowAnimation_wallpaperCloseExitAnimation; 1584 break; 1585 case TRANSIT_WALLPAPER_INTRA_OPEN: 1586 animAttr = enter 1587 ? WindowAnimation_wallpaperIntraOpenEnterAnimation 1588 : WindowAnimation_wallpaperIntraOpenExitAnimation; 1589 break; 1590 case TRANSIT_WALLPAPER_INTRA_CLOSE: 1591 animAttr = enter 1592 ? WindowAnimation_wallpaperIntraCloseEnterAnimation 1593 : WindowAnimation_wallpaperIntraCloseExitAnimation; 1594 break; 1595 case TRANSIT_TASK_OPEN_BEHIND: 1596 animAttr = enter 1597 ? WindowAnimation_launchTaskBehindSourceAnimation 1598 : WindowAnimation_launchTaskBehindTargetAnimation; 1599 } 1600 a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null; 1601 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 1602 "applyAnimation:" 1603 + " anim=" + a 1604 + " animAttr=0x" + Integer.toHexString(animAttr) 1605 + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter 1606 + " Callers=" + Debug.getCallers(3)); 1607 } 1608 return a; 1609 } 1610 getAppStackClipMode()1611 int getAppStackClipMode() { 1612 return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH 1613 || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS 1614 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL 1615 ? STACK_CLIP_NONE 1616 : STACK_CLIP_AFTER_ANIM; 1617 } 1618 postAnimationCallback()1619 void postAnimationCallback() { 1620 if (mNextAppTransitionCallback != null) { 1621 mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, 1622 mNextAppTransitionCallback)); 1623 mNextAppTransitionCallback = null; 1624 } 1625 } 1626 overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, IRemoteCallback startedCallback)1627 void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, 1628 IRemoteCallback startedCallback) { 1629 if (isTransitionSet()) { 1630 clear(); 1631 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; 1632 mNextAppTransitionPackage = packageName; 1633 mNextAppTransitionEnter = enterAnim; 1634 mNextAppTransitionExit = exitAnim; 1635 postAnimationCallback(); 1636 mNextAppTransitionCallback = startedCallback; 1637 } else { 1638 postAnimationCallback(); 1639 } 1640 } 1641 overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, int startHeight)1642 void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, 1643 int startHeight) { 1644 if (isTransitionSet()) { 1645 clear(); 1646 mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP; 1647 putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null); 1648 postAnimationCallback(); 1649 } 1650 } 1651 overridePendingAppTransitionClipReveal(int startX, int startY, int startWidth, int startHeight)1652 void overridePendingAppTransitionClipReveal(int startX, int startY, 1653 int startWidth, int startHeight) { 1654 if (isTransitionSet()) { 1655 clear(); 1656 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL; 1657 putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null); 1658 postAnimationCallback(); 1659 } 1660 } 1661 overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp)1662 void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, 1663 IRemoteCallback startedCallback, boolean scaleUp) { 1664 if (isTransitionSet()) { 1665 clear(); 1666 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP 1667 : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN; 1668 mNextAppTransitionScaleUp = scaleUp; 1669 putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb); 1670 postAnimationCallback(); 1671 mNextAppTransitionCallback = startedCallback; 1672 } else { 1673 postAnimationCallback(); 1674 } 1675 } 1676 overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp)1677 void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY, 1678 int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) { 1679 if (isTransitionSet()) { 1680 clear(); 1681 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1682 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1683 mNextAppTransitionScaleUp = scaleUp; 1684 putDefaultNextAppTransitionCoordinates(startX, startY, targetWidth, targetHeight, 1685 srcThumb); 1686 postAnimationCallback(); 1687 mNextAppTransitionCallback = startedCallback; 1688 } else { 1689 postAnimationCallback(); 1690 } 1691 } 1692 overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, boolean scaleUp)1693 public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs, 1694 IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback, 1695 boolean scaleUp) { 1696 if (isTransitionSet()) { 1697 clear(); 1698 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1699 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1700 mNextAppTransitionScaleUp = scaleUp; 1701 if (specs != null) { 1702 for (int i = 0; i < specs.length; i++) { 1703 AppTransitionAnimationSpec spec = specs[i]; 1704 if (spec != null) { 1705 mNextAppTransitionAnimationsSpecs.put(spec.taskId, spec); 1706 if (i == 0) { 1707 // In full screen mode, the transition code depends on the default spec 1708 // to be set. 1709 Rect rect = spec.rect; 1710 putDefaultNextAppTransitionCoordinates(rect.left, rect.top, 1711 rect.width(), rect.height(), spec.bitmap); 1712 } 1713 } 1714 } 1715 } 1716 postAnimationCallback(); 1717 mNextAppTransitionCallback = onAnimationStartedCallback; 1718 mAnimationFinishedCallback = onAnimationFinishedCallback; 1719 } else { 1720 postAnimationCallback(); 1721 } 1722 } 1723 overridePendingAppTransitionMultiThumbFuture( IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, boolean scaleUp)1724 void overridePendingAppTransitionMultiThumbFuture( 1725 IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback, 1726 boolean scaleUp) { 1727 if (isTransitionSet()) { 1728 clear(); 1729 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP 1730 : NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; 1731 mNextAppTransitionAnimationsSpecsFuture = specsFuture; 1732 mNextAppTransitionScaleUp = scaleUp; 1733 mNextAppTransitionFutureCallback = callback; 1734 } 1735 } 1736 overrideInPlaceAppTransition(String packageName, int anim)1737 void overrideInPlaceAppTransition(String packageName, int anim) { 1738 if (isTransitionSet()) { 1739 clear(); 1740 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE; 1741 mNextAppTransitionPackage = packageName; 1742 mNextAppTransitionInPlace = anim; 1743 } else { 1744 postAnimationCallback(); 1745 } 1746 } 1747 1748 /** 1749 * If a future is set for the app transition specs, fetch it in another thread. 1750 */ fetchAppTransitionSpecsFromFuture()1751 private void fetchAppTransitionSpecsFromFuture() { 1752 if (mNextAppTransitionAnimationsSpecsFuture != null) { 1753 mNextAppTransitionAnimationsSpecsPending = true; 1754 final IAppTransitionAnimationSpecsFuture future 1755 = mNextAppTransitionAnimationsSpecsFuture; 1756 mNextAppTransitionAnimationsSpecsFuture = null; 1757 mDefaultExecutor.execute(new Runnable() { 1758 @Override 1759 public void run() { 1760 AppTransitionAnimationSpec[] specs = null; 1761 try { 1762 specs = future.get(); 1763 } catch (RemoteException e) { 1764 Slog.w(TAG, "Failed to fetch app transition specs: " + e); 1765 } 1766 synchronized (mService.mWindowMap) { 1767 mNextAppTransitionAnimationsSpecsPending = false; 1768 overridePendingAppTransitionMultiThumb(specs, 1769 mNextAppTransitionFutureCallback, null /* finishedCallback */, 1770 mNextAppTransitionScaleUp); 1771 mNextAppTransitionFutureCallback = null; 1772 if (specs != null) { 1773 mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp); 1774 } 1775 } 1776 mService.requestTraversal(); 1777 } 1778 }); 1779 } 1780 } 1781 1782 @Override toString()1783 public String toString() { 1784 return "mNextAppTransition=" + appTransitionToString(mNextAppTransition); 1785 } 1786 1787 /** 1788 * Returns the human readable name of a window transition. 1789 * 1790 * @param transition The window transition. 1791 * @return The transition symbolic name. 1792 */ appTransitionToString(int transition)1793 public static String appTransitionToString(int transition) { 1794 switch (transition) { 1795 case TRANSIT_UNSET: { 1796 return "TRANSIT_UNSET"; 1797 } 1798 case TRANSIT_NONE: { 1799 return "TRANSIT_NONE"; 1800 } 1801 case TRANSIT_ACTIVITY_OPEN: { 1802 return "TRANSIT_ACTIVITY_OPEN"; 1803 } 1804 case TRANSIT_ACTIVITY_CLOSE: { 1805 return "TRANSIT_ACTIVITY_CLOSE"; 1806 } 1807 case TRANSIT_TASK_OPEN: { 1808 return "TRANSIT_TASK_OPEN"; 1809 } 1810 case TRANSIT_TASK_CLOSE: { 1811 return "TRANSIT_TASK_CLOSE"; 1812 } 1813 case TRANSIT_TASK_TO_FRONT: { 1814 return "TRANSIT_TASK_TO_FRONT"; 1815 } 1816 case TRANSIT_TASK_TO_BACK: { 1817 return "TRANSIT_TASK_TO_BACK"; 1818 } 1819 case TRANSIT_WALLPAPER_CLOSE: { 1820 return "TRANSIT_WALLPAPER_CLOSE"; 1821 } 1822 case TRANSIT_WALLPAPER_OPEN: { 1823 return "TRANSIT_WALLPAPER_OPEN"; 1824 } 1825 case TRANSIT_WALLPAPER_INTRA_OPEN: { 1826 return "TRANSIT_WALLPAPER_INTRA_OPEN"; 1827 } 1828 case TRANSIT_WALLPAPER_INTRA_CLOSE: { 1829 return "TRANSIT_WALLPAPER_INTRA_CLOSE"; 1830 } 1831 case TRANSIT_TASK_OPEN_BEHIND: { 1832 return "TRANSIT_TASK_OPEN_BEHIND"; 1833 } 1834 case TRANSIT_ACTIVITY_RELAUNCH: { 1835 return "TRANSIT_ACTIVITY_RELAUNCH"; 1836 } 1837 case TRANSIT_DOCK_TASK_FROM_RECENTS: { 1838 return "TRANSIT_DOCK_TASK_FROM_RECENTS"; 1839 } 1840 default: { 1841 return "<UNKNOWN>"; 1842 } 1843 } 1844 } 1845 appStateToString()1846 private String appStateToString() { 1847 switch (mAppTransitionState) { 1848 case APP_STATE_IDLE: 1849 return "APP_STATE_IDLE"; 1850 case APP_STATE_READY: 1851 return "APP_STATE_READY"; 1852 case APP_STATE_RUNNING: 1853 return "APP_STATE_RUNNING"; 1854 case APP_STATE_TIMEOUT: 1855 return "APP_STATE_TIMEOUT"; 1856 default: 1857 return "unknown state=" + mAppTransitionState; 1858 } 1859 } 1860 transitTypeToString()1861 private String transitTypeToString() { 1862 switch (mNextAppTransitionType) { 1863 case NEXT_TRANSIT_TYPE_NONE: 1864 return "NEXT_TRANSIT_TYPE_NONE"; 1865 case NEXT_TRANSIT_TYPE_CUSTOM: 1866 return "NEXT_TRANSIT_TYPE_CUSTOM"; 1867 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE: 1868 return "NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE"; 1869 case NEXT_TRANSIT_TYPE_SCALE_UP: 1870 return "NEXT_TRANSIT_TYPE_SCALE_UP"; 1871 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 1872 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP"; 1873 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 1874 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN"; 1875 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: 1876 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP"; 1877 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: 1878 return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN"; 1879 default: 1880 return "unknown type=" + mNextAppTransitionType; 1881 } 1882 } 1883 1884 @Override dump(PrintWriter pw, String prefix)1885 public void dump(PrintWriter pw, String prefix) { 1886 pw.print(prefix); pw.println(this); 1887 pw.print(prefix); pw.print("mAppTransitionState="); pw.println(appStateToString()); 1888 if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) { 1889 pw.print(prefix); pw.print("mNextAppTransitionType="); 1890 pw.println(transitTypeToString()); 1891 } 1892 switch (mNextAppTransitionType) { 1893 case NEXT_TRANSIT_TYPE_CUSTOM: 1894 pw.print(prefix); pw.print("mNextAppTransitionPackage="); 1895 pw.println(mNextAppTransitionPackage); 1896 pw.print(prefix); pw.print("mNextAppTransitionEnter=0x"); 1897 pw.print(Integer.toHexString(mNextAppTransitionEnter)); 1898 pw.print(" mNextAppTransitionExit=0x"); 1899 pw.println(Integer.toHexString(mNextAppTransitionExit)); 1900 break; 1901 case NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE: 1902 pw.print(prefix); pw.print("mNextAppTransitionPackage="); 1903 pw.println(mNextAppTransitionPackage); 1904 pw.print(prefix); pw.print("mNextAppTransitionInPlace=0x"); 1905 pw.print(Integer.toHexString(mNextAppTransitionInPlace)); 1906 break; 1907 case NEXT_TRANSIT_TYPE_SCALE_UP: { 1908 getDefaultNextAppTransitionStartRect(mTmpRect); 1909 pw.print(prefix); pw.print("mNextAppTransitionStartX="); 1910 pw.print(mTmpRect.left); 1911 pw.print(" mNextAppTransitionStartY="); 1912 pw.println(mTmpRect.top); 1913 pw.print(prefix); pw.print("mNextAppTransitionStartWidth="); 1914 pw.print(mTmpRect.width()); 1915 pw.print(" mNextAppTransitionStartHeight="); 1916 pw.println(mTmpRect.height()); 1917 break; 1918 } 1919 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 1920 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 1921 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP: 1922 case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN: { 1923 pw.print(prefix); pw.print("mDefaultNextAppTransitionAnimationSpec="); 1924 pw.println(mDefaultNextAppTransitionAnimationSpec); 1925 pw.print(prefix); pw.print("mNextAppTransitionAnimationsSpecs="); 1926 pw.println(mNextAppTransitionAnimationsSpecs); 1927 pw.print(prefix); pw.print("mNextAppTransitionScaleUp="); 1928 pw.println(mNextAppTransitionScaleUp); 1929 break; 1930 } 1931 } 1932 if (mNextAppTransitionCallback != null) { 1933 pw.print(prefix); pw.print("mNextAppTransitionCallback="); 1934 pw.println(mNextAppTransitionCallback); 1935 } 1936 if (mLastUsedAppTransition != TRANSIT_NONE) { 1937 pw.print(prefix); pw.print("mLastUsedAppTransition="); 1938 pw.println(appTransitionToString(mLastUsedAppTransition)); 1939 pw.print(prefix); pw.print("mLastOpeningApp="); 1940 pw.println(mLastOpeningApp); 1941 pw.print(prefix); pw.print("mLastClosingApp="); 1942 pw.println(mLastClosingApp); 1943 } 1944 } 1945 setCurrentUser(int newUserId)1946 public void setCurrentUser(int newUserId) { 1947 mCurrentUserId = newUserId; 1948 } 1949 1950 /** 1951 * @return true if transition is not running and should not be skipped, false if transition is 1952 * already running 1953 */ prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent)1954 boolean prepareAppTransitionLocked(int transit, boolean alwaysKeepCurrent) { 1955 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Prepare app transition:" 1956 + " transit=" + appTransitionToString(transit) 1957 + " " + this 1958 + " alwaysKeepCurrent=" + alwaysKeepCurrent 1959 + " Callers=" + Debug.getCallers(3)); 1960 if (!isTransitionSet() || mNextAppTransition == TRANSIT_NONE) { 1961 setAppTransition(transit); 1962 } else if (!alwaysKeepCurrent) { 1963 if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) { 1964 // Opening a new task always supersedes a close for the anim. 1965 setAppTransition(transit); 1966 } else if (transit == TRANSIT_ACTIVITY_OPEN 1967 && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) { 1968 // Opening a new activity always supersedes a close for the anim. 1969 setAppTransition(transit); 1970 } 1971 } 1972 boolean prepared = prepare(); 1973 if (isTransitionSet()) { 1974 mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); 1975 mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); 1976 } 1977 return prepared; 1978 } 1979 1980 /** 1981 * @return whether the transition should show the thumbnail being scaled down. 1982 */ shouldScaleDownThumbnailTransition(int uiMode, int orientation)1983 private boolean shouldScaleDownThumbnailTransition(int uiMode, int orientation) { 1984 return isTvUiMode(uiMode) 1985 || mGridLayoutRecentsEnabled 1986 || orientation == Configuration.ORIENTATION_PORTRAIT; 1987 } 1988 1989 /** 1990 * @return whether the specified {@param uiMode} is the TV mode. 1991 */ isTvUiMode(int uiMode)1992 private boolean isTvUiMode(int uiMode) { 1993 return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0; 1994 } 1995 } 1996