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