AppTransition.java revision 2820c4523cd37b4a9c5d33e558d39b798830e7e5
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wm; 18 19import android.content.Context; 20import android.content.res.Configuration; 21import android.graphics.Bitmap; 22import android.graphics.Point; 23import android.graphics.Rect; 24import android.os.Debug; 25import android.os.Handler; 26import android.os.IRemoteCallback; 27import android.os.SystemProperties; 28import android.util.Slog; 29import android.view.WindowManager; 30import android.view.animation.AlphaAnimation; 31import android.view.animation.Animation; 32import android.view.animation.AnimationSet; 33import android.view.animation.AnimationUtils; 34import android.view.animation.ClipRectAnimation; 35import android.view.animation.Interpolator; 36import android.view.animation.ScaleAnimation; 37 38import android.view.animation.TranslateAnimation; 39import com.android.internal.util.DumpUtils.Dump; 40import com.android.server.AttributeCache; 41import com.android.server.wm.WindowManagerService.H; 42 43import java.io.PrintWriter; 44 45import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation; 46import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation; 47import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation; 48import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; 49import static com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation; 50import static com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation; 51import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation; 52import static com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation; 53import static com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation; 54import static com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation; 55import static com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation; 56import static com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation; 57import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation; 58import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; 59import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation; 60import static com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation; 61import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation; 62import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; 63import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation; 64import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation; 65 66// State management of app transitions. When we are preparing for a 67// transition, mNextAppTransition will be the kind of transition to 68// perform or TRANSIT_NONE if we are not waiting. If we are waiting, 69// mOpeningApps and mClosingApps are the lists of tokens that will be 70// made visible or hidden at the next transition. 71public class AppTransition implements Dump { 72 private static final String TAG = "AppTransition"; 73 private static final boolean DEBUG_APP_TRANSITIONS = 74 WindowManagerService.DEBUG_APP_TRANSITIONS; 75 private static final boolean DEBUG_ANIM = WindowManagerService.DEBUG_ANIM; 76 77 /** Bit mask that is set for all enter transition. */ 78 public static final int TRANSIT_ENTER_MASK = 0x1000; 79 80 /** Bit mask that is set for all exit transitions. */ 81 public static final int TRANSIT_EXIT_MASK = 0x2000; 82 83 /** Not set up for a transition. */ 84 public static final int TRANSIT_UNSET = -1; 85 /** No animation for transition. */ 86 public static final int TRANSIT_NONE = 0; 87 /** A window in a new activity is being opened on top of an existing one in the same task. */ 88 public static final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK; 89 /** The window in the top-most activity is being closed to reveal the 90 * previous activity in the same task. */ 91 public static final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK; 92 /** A window in a new task is being opened on top of an existing one 93 * in another activity's task. */ 94 public static final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK; 95 /** A window in the top-most activity is being closed to reveal the 96 * previous activity in a different task. */ 97 public static final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK; 98 /** A window in an existing task is being displayed on top of an existing one 99 * in another activity's task. */ 100 public static final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK; 101 /** A window in an existing task is being put below all other tasks. */ 102 public static final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK; 103 /** A window in a new activity that doesn't have a wallpaper is being opened on top of one that 104 * does, effectively closing the wallpaper. */ 105 public static final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK; 106 /** A window in a new activity that does have a wallpaper is being opened on one that didn't, 107 * effectively opening the wallpaper. */ 108 public static final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK; 109 /** A window in a new activity is being opened on top of an existing one, and both are on top 110 * of the wallpaper. */ 111 public static final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK; 112 /** The window in the top-most activity is being closed to reveal the previous activity, and 113 * both are on top of the wallpaper. */ 114 public static final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK; 115 116 /** Fraction of animation at which the recents thumbnail becomes completely transparent */ 117 private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f; 118 119 private static final long DEFAULT_APP_TRANSITION_DURATION = 250; 120 121 private final Context mContext; 122 private final Handler mH; 123 124 private int mNextAppTransition = TRANSIT_UNSET; 125 126 private static final int NEXT_TRANSIT_TYPE_NONE = 0; 127 private static final int NEXT_TRANSIT_TYPE_CUSTOM = 1; 128 private static final int NEXT_TRANSIT_TYPE_SCALE_UP = 2; 129 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP = 3; 130 private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4; 131 private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; 132 133 // These are the possible states for the enter/exit activities during a thumbnail transition 134 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; 135 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; 136 private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; 137 private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; 138 139 private String mNextAppTransitionPackage; 140 private Bitmap mNextAppTransitionThumbnail; 141 // Used for thumbnail transitions. True if we're scaling up, false if scaling down 142 private boolean mNextAppTransitionScaleUp; 143 private IRemoteCallback mNextAppTransitionCallback; 144 private int mNextAppTransitionEnter; 145 private int mNextAppTransitionExit; 146 private int mNextAppTransitionStartX; 147 private int mNextAppTransitionStartY; 148 private int mNextAppTransitionStartWidth; 149 private int mNextAppTransitionStartHeight; 150 151 private Rect mTmpFromClipRect = new Rect(); 152 private Rect mTmpToClipRect = new Rect(); 153 154 private final static int APP_STATE_IDLE = 0; 155 private final static int APP_STATE_READY = 1; 156 private final static int APP_STATE_RUNNING = 2; 157 private final static int APP_STATE_TIMEOUT = 3; 158 private int mAppTransitionState = APP_STATE_IDLE; 159 160 private final int mConfigShortAnimTime; 161 private final Interpolator mDecelerateInterpolator; 162 private final Interpolator mThumbnailFadeoutInterpolator; 163 164 private int mCurrentUserId = 0; 165 private boolean mUseAlternateThumbnailAnimation; 166 167 AppTransition(Context context, Handler h) { 168 mContext = context; 169 mH = h; 170 mUseAlternateThumbnailAnimation = 171 SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false); 172 mConfigShortAnimTime = context.getResources().getInteger( 173 com.android.internal.R.integer.config_shortAnimTime); 174 mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, 175 com.android.internal.R.interpolator.decelerate_cubic); 176 mThumbnailFadeoutInterpolator = new Interpolator() { 177 @Override 178 public float getInterpolation(float input) { 179 // Linear response for first fraction, then complete after that. 180 if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { 181 return input / RECENTS_THUMBNAIL_FADEOUT_FRACTION; 182 } 183 return 1.0f; 184 } 185 }; 186 } 187 188 boolean isTransitionSet() { 189 return mNextAppTransition != TRANSIT_UNSET; 190 } 191 192 boolean isTransitionNone() { 193 return mNextAppTransition == TRANSIT_NONE; 194 } 195 196 boolean isTransitionEqual(int transit) { 197 return mNextAppTransition == transit; 198 } 199 200 int getAppTransition() { 201 return mNextAppTransition; 202 } 203 204 void setAppTransition(int transit) { 205 mNextAppTransition = transit; 206 } 207 208 boolean isReady() { 209 return mAppTransitionState == APP_STATE_READY 210 || mAppTransitionState == APP_STATE_TIMEOUT; 211 } 212 213 void setReady() { 214 mAppTransitionState = APP_STATE_READY; 215 } 216 217 boolean isRunning() { 218 return mAppTransitionState == APP_STATE_RUNNING; 219 } 220 221 void setIdle() { 222 mAppTransitionState = APP_STATE_IDLE; 223 } 224 225 boolean isTimeout() { 226 return mAppTransitionState == APP_STATE_TIMEOUT; 227 } 228 229 void setTimeout() { 230 mAppTransitionState = APP_STATE_TIMEOUT; 231 } 232 233 Bitmap getNextAppTransitionThumbnail() { 234 return mNextAppTransitionThumbnail; 235 } 236 237 void getStartingPoint(Point outPoint) { 238 outPoint.x = mNextAppTransitionStartX; 239 outPoint.y = mNextAppTransitionStartY; 240 } 241 242 void prepare() { 243 if (!isRunning()) { 244 mAppTransitionState = APP_STATE_IDLE; 245 } 246 } 247 248 void goodToGo() { 249 mNextAppTransition = TRANSIT_UNSET; 250 mAppTransitionState = APP_STATE_RUNNING; 251 } 252 253 void clear() { 254 mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; 255 mNextAppTransitionPackage = null; 256 mNextAppTransitionThumbnail = null; 257 } 258 259 void freeze() { 260 setAppTransition(AppTransition.TRANSIT_UNSET); 261 clear(); 262 setReady(); 263 } 264 265 private AttributeCache.Entry getCachedAnimations(WindowManager.LayoutParams lp) { 266 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg=" 267 + (lp != null ? lp.packageName : null) 268 + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null)); 269 if (lp != null && lp.windowAnimations != 0) { 270 // If this is a system resource, don't try to load it from the 271 // application resources. It is nice to avoid loading application 272 // resources if we can. 273 String packageName = lp.packageName != null ? lp.packageName : "android"; 274 int resId = lp.windowAnimations; 275 if ((resId&0xFF000000) == 0x01000000) { 276 packageName = "android"; 277 } 278 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" 279 + packageName); 280 return AttributeCache.instance().get(packageName, resId, 281 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId); 282 } 283 return null; 284 } 285 286 private AttributeCache.Entry getCachedAnimations(String packageName, int resId) { 287 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: package=" 288 + packageName + " resId=0x" + Integer.toHexString(resId)); 289 if (packageName != null) { 290 if ((resId&0xFF000000) == 0x01000000) { 291 packageName = "android"; 292 } 293 if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package=" 294 + packageName); 295 return AttributeCache.instance().get(packageName, resId, 296 com.android.internal.R.styleable.WindowAnimation, mCurrentUserId); 297 } 298 return null; 299 } 300 301 Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) { 302 int anim = 0; 303 Context context = mContext; 304 if (animAttr >= 0) { 305 AttributeCache.Entry ent = getCachedAnimations(lp); 306 if (ent != null) { 307 context = ent.context; 308 anim = ent.array.getResourceId(animAttr, 0); 309 } 310 } 311 if (anim != 0) { 312 return AnimationUtils.loadAnimation(context, anim); 313 } 314 return null; 315 } 316 317 private Animation loadAnimation(String packageName, int resId) { 318 int anim = 0; 319 Context context = mContext; 320 if (resId >= 0) { 321 AttributeCache.Entry ent = getCachedAnimations(packageName, resId); 322 if (ent != null) { 323 context = ent.context; 324 anim = resId; 325 } 326 } 327 if (anim != 0) { 328 return AnimationUtils.loadAnimation(context, anim); 329 } 330 return null; 331 } 332 333 /** 334 * Compute the pivot point for an animation that is scaling from a small 335 * rect on screen to a larger rect. The pivot point varies depending on 336 * the distance between the inner and outer edges on both sides. This 337 * function computes the pivot point for one dimension. 338 * @param startPos Offset from left/top edge of outer rectangle to 339 * left/top edge of inner rectangle. 340 * @param finalScale The scaling factor between the size of the outer 341 * and inner rectangles. 342 */ 343 private static float computePivot(int startPos, float finalScale) { 344 final float denom = finalScale-1; 345 if (Math.abs(denom) < .0001f) { 346 return startPos; 347 } 348 return -startPos / denom; 349 } 350 351 private Animation createScaleUpAnimationLocked(int transit, boolean enter, 352 int appWidth, int appHeight) { 353 Animation a = null; 354 if (enter) { 355 // Entering app zooms out from the center of the initial rect. 356 float scaleW = mNextAppTransitionStartWidth / (float) appWidth; 357 float scaleH = mNextAppTransitionStartHeight / (float) appHeight; 358 Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1, 359 computePivot(mNextAppTransitionStartX, scaleW), 360 computePivot(mNextAppTransitionStartY, scaleH)); 361 scale.setInterpolator(mDecelerateInterpolator); 362 363 Animation alpha = new AlphaAnimation(0, 1); 364 alpha.setInterpolator(mThumbnailFadeoutInterpolator); 365 366 AnimationSet set = new AnimationSet(false); 367 set.addAnimation(scale); 368 set.addAnimation(alpha); 369 set.setDetachWallpaper(true); 370 a = set; 371 } else if (transit == TRANSIT_WALLPAPER_INTRA_OPEN || 372 transit == TRANSIT_WALLPAPER_INTRA_CLOSE) { 373 // If we are on top of the wallpaper, we need an animation that 374 // correctly handles the wallpaper staying static behind all of 375 // the animated elements. To do this, will just have the existing 376 // element fade out. 377 a = new AlphaAnimation(1, 0); 378 a.setDetachWallpaper(true); 379 } else { 380 // For normal animations, the exiting element just holds in place. 381 a = new AlphaAnimation(1, 1); 382 } 383 384 // Pick the desired duration. If this is an inter-activity transition, 385 // it is the standard duration for that. Otherwise we use the longer 386 // task transition duration. 387 final long duration; 388 switch (transit) { 389 case TRANSIT_ACTIVITY_OPEN: 390 case TRANSIT_ACTIVITY_CLOSE: 391 duration = mConfigShortAnimTime; 392 break; 393 default: 394 duration = DEFAULT_APP_TRANSITION_DURATION; 395 break; 396 } 397 a.setDuration(duration); 398 a.setFillAfter(true); 399 a.setInterpolator(mDecelerateInterpolator); 400 a.initialize(appWidth, appHeight, appWidth, appHeight); 401 return a; 402 } 403 404 /** 405 * Prepares the specified animation with a standard duration, interpolator, etc. 406 */ 407 Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { 408 // Pick the desired duration. If this is an inter-activity transition, 409 // it is the standard duration for that. Otherwise we use the longer 410 // task transition duration. 411 final long duration; 412 switch (transit) { 413 case TRANSIT_ACTIVITY_OPEN: 414 case TRANSIT_ACTIVITY_CLOSE: 415 duration = mConfigShortAnimTime; 416 break; 417 default: 418 duration = DEFAULT_APP_TRANSITION_DURATION; 419 break; 420 } 421 a.setDuration(duration); 422 a.setFillAfter(true); 423 a.setInterpolator(mDecelerateInterpolator); 424 a.initialize(appWidth, appHeight, appWidth, appHeight); 425 return a; 426 } 427 428 /** 429 * Return the current thumbnail transition state. 430 */ 431 int getThumbnailTransitionState(boolean enter) { 432 if (enter) { 433 if (mNextAppTransitionScaleUp) { 434 return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; 435 } else { 436 return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; 437 } 438 } else { 439 if (mNextAppTransitionScaleUp) { 440 return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; 441 } else { 442 return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; 443 } 444 } 445 } 446 447 /** 448 * This animation runs for the thumbnail that gets cross faded with the enter/exit activity 449 * when a thumbnail is specified with the activity options. 450 */ 451 Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) { 452 Animation a; 453 final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); 454 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 455 final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); 456 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 457 458 if (mNextAppTransitionScaleUp) { 459 // Animation for the thumbnail zooming from its initial size to the full screen 460 float scaleW = appWidth / thumbWidth; 461 float scaleH = appHeight / thumbHeight; 462 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 463 computePivot(mNextAppTransitionStartX, 1 / scaleW), 464 computePivot(mNextAppTransitionStartY, 1 / scaleH)); 465 scale.setInterpolator(mDecelerateInterpolator); 466 467 Animation alpha = new AlphaAnimation(1, 0); 468 alpha.setInterpolator(mThumbnailFadeoutInterpolator); 469 470 // This AnimationSet uses the Interpolators assigned above. 471 AnimationSet set = new AnimationSet(false); 472 set.addAnimation(scale); 473 set.addAnimation(alpha); 474 a = set; 475 } else { 476 // Animation for the thumbnail zooming down from the full screen to its final size 477 float scaleW = appWidth / thumbWidth; 478 float scaleH = appHeight / thumbHeight; 479 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 480 computePivot(mNextAppTransitionStartX, 1 / scaleW), 481 computePivot(mNextAppTransitionStartY, 1 / scaleH)); 482 } 483 484 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 485 } 486 487 /** 488 * This alternate animation is created when we are doing a thumbnail transition, for the 489 * activity that is leaving, and the activity that is entering. 490 */ 491 Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, 492 int appHeight, int orientation, int transit, 493 Rect containingFrame, Rect contentInsets) { 494 Animation a; 495 final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); 496 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 497 final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); 498 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 499 500 // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions 501 float scale = 1f; 502 int scaledTopDecor = 0; 503 504 switch (thumbTransitState) { 505 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { 506 // Entering app scales up with the thumbnail 507 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 508 // In portrait, we scale the width and clip to the top/left square 509 scale = thumbWidth / appWidth; 510 scaledTopDecor = (int) (scale * contentInsets.top); 511 int unscaledThumbHeight = (int) (thumbHeight / scale); 512 mTmpFromClipRect.set(containingFrame); 513 mTmpFromClipRect.bottom = (mTmpFromClipRect.top + unscaledThumbHeight); 514 mTmpToClipRect.set(containingFrame); 515 } else { 516 // In landscape, we scale the height and clip to the top/left square 517 scale = thumbHeight / (appHeight - contentInsets.top); 518 scaledTopDecor = (int) (scale * contentInsets.top); 519 int unscaledThumbWidth = (int) (thumbWidth / scale); 520 mTmpFromClipRect.set(containingFrame); 521 mTmpFromClipRect.right = (mTmpFromClipRect.left + unscaledThumbWidth); 522 mTmpToClipRect.set(containingFrame); 523 } 524 525 Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1, 526 computePivot(mNextAppTransitionStartX, scale), 527 computePivot(mNextAppTransitionStartY, scale)); 528 Animation alphaAnim = new AlphaAnimation(1, 1); 529 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 530 Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0); 531 532 AnimationSet set = new AnimationSet(true); 533 set.addAnimation(alphaAnim); 534 set.addAnimation(clipAnim); 535 set.addAnimation(scaleAnim); 536 set.addAnimation(translateAnim); 537 a = set; 538 break; 539 } 540 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 541 // Exiting app while the thumbnail is scaling up should fade 542 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 543 // Fade out while bringing up selected activity. This keeps the 544 // current activity from showing through a launching wallpaper 545 // activity. 546 a = new AlphaAnimation(1, 0); 547 } else { 548 // noop animation 549 a = new AlphaAnimation(1, 1); 550 } 551 break; 552 } 553 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 554 // Entering the other app, it should just be visible while we scale the thumbnail 555 // down above it 556 a = new AlphaAnimation(1, 1); 557 break; 558 } 559 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 560 // Exiting the current app, the app should scale down with the thumbnail 561 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 562 // In portrait, we scale the width and clip to the top/left square 563 scale = thumbWidth / appWidth; 564 scaledTopDecor = (int) (scale * contentInsets.top); 565 int unscaledThumbHeight = (int) (thumbHeight / scale); 566 mTmpFromClipRect.set(containingFrame); 567 mTmpToClipRect.set(containingFrame); 568 mTmpToClipRect.bottom = (mTmpToClipRect.top + unscaledThumbHeight); 569 } else { 570 // In landscape, we scale the height and clip to the top/left square 571 scale = thumbHeight / (appHeight - contentInsets.top); 572 scaledTopDecor = (int) (scale * contentInsets.top); 573 int unscaledThumbWidth = (int) (thumbWidth / scale); 574 mTmpFromClipRect.set(containingFrame); 575 mTmpToClipRect.set(containingFrame); 576 mTmpToClipRect.right = (mTmpToClipRect.left + unscaledThumbWidth); 577 } 578 579 Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale, 580 computePivot(mNextAppTransitionStartX, scale), 581 computePivot(mNextAppTransitionStartY, scale)); 582 Animation alphaAnim = new AlphaAnimation(1, 1); 583 Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); 584 Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor); 585 586 AnimationSet set = new AnimationSet(true); 587 set.addAnimation(alphaAnim); 588 set.addAnimation(clipAnim); 589 set.addAnimation(scaleAnim); 590 set.addAnimation(translateAnim); 591 592 a = set; 593 a.setZAdjustment(Animation.ZORDER_TOP); 594 break; 595 } 596 default: 597 throw new RuntimeException("Invalid thumbnail transition state"); 598 } 599 600 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 601 } 602 603 /** 604 * This animation is created when we are doing a thumbnail transition, for the activity that is 605 * leaving, and the activity that is entering. 606 */ 607 Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, 608 int appHeight, int transit) { 609 Animation a; 610 final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); 611 final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; 612 final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); 613 final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; 614 615 switch (thumbTransitState) { 616 case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { 617 // Entering app scales up with the thumbnail 618 float scaleW = thumbWidth / appWidth; 619 float scaleH = thumbHeight / appHeight; 620 a = new ScaleAnimation(scaleW, 1, scaleH, 1, 621 computePivot(mNextAppTransitionStartX, scaleW), 622 computePivot(mNextAppTransitionStartY, scaleH)); 623 break; 624 } 625 case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { 626 // Exiting app while the thumbnail is scaling up should fade or stay in place 627 if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { 628 // Fade out while bringing up selected activity. This keeps the 629 // current activity from showing through a launching wallpaper 630 // activity. 631 a = new AlphaAnimation(1, 0); 632 } else { 633 // noop animation 634 a = new AlphaAnimation(1, 1); 635 } 636 break; 637 } 638 case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { 639 // Entering the other app, it should just be visible while we scale the thumbnail 640 // down above it 641 a = new AlphaAnimation(1, 1); 642 break; 643 } 644 case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { 645 // Exiting the current app, the app should scale down with the thumbnail 646 float scaleW = thumbWidth / appWidth; 647 float scaleH = thumbHeight / appHeight; 648 Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, 649 computePivot(mNextAppTransitionStartX, scaleW), 650 computePivot(mNextAppTransitionStartY, scaleH)); 651 652 Animation alpha = new AlphaAnimation(1, 0); 653 654 AnimationSet set = new AnimationSet(true); 655 set.addAnimation(scale); 656 set.addAnimation(alpha); 657 set.setZAdjustment(Animation.ZORDER_TOP); 658 a = set; 659 break; 660 } 661 default: 662 throw new RuntimeException("Invalid thumbnail transition state"); 663 } 664 665 return prepareThumbnailAnimation(a, appWidth, appHeight, transit); 666 } 667 668 669 Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, 670 int appWidth, int appHeight, int orientation, 671 Rect containingFrame, Rect contentInsets) { 672 Animation a; 673 if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { 674 a = loadAnimation(mNextAppTransitionPackage, enter ? 675 mNextAppTransitionEnter : mNextAppTransitionExit); 676 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 677 "applyAnimation:" 678 + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" 679 + " transit=" + transit + " isEntrance=" + enter 680 + " Callers=" + Debug.getCallers(3)); 681 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { 682 a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight); 683 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 684 "applyAnimation:" 685 + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" 686 + " transit=" + transit + " isEntrance=" + enter 687 + " Callers=" + Debug.getCallers(3)); 688 } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || 689 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { 690 mNextAppTransitionScaleUp = 691 (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); 692 if (mUseAlternateThumbnailAnimation) { 693 a = createAlternateThumbnailEnterExitAnimationLocked( 694 getThumbnailTransitionState(enter), appWidth, appHeight, orientation, 695 transit, containingFrame, contentInsets); 696 } else { 697 a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), 698 appWidth, appHeight, transit); 699 } 700 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { 701 String animName = mNextAppTransitionScaleUp ? 702 "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; 703 Slog.v(TAG, "applyAnimation:" 704 + " anim=" + a + " nextAppTransition=" + animName 705 + " transit=" + transit + " isEntrance=" + enter 706 + " Callers=" + Debug.getCallers(3)); 707 } 708 } else { 709 int animAttr = 0; 710 switch (transit) { 711 case TRANSIT_ACTIVITY_OPEN: 712 animAttr = enter 713 ? WindowAnimation_activityOpenEnterAnimation 714 : WindowAnimation_activityOpenExitAnimation; 715 break; 716 case TRANSIT_ACTIVITY_CLOSE: 717 animAttr = enter 718 ? WindowAnimation_activityCloseEnterAnimation 719 : WindowAnimation_activityCloseExitAnimation; 720 break; 721 case TRANSIT_TASK_OPEN: 722 animAttr = enter 723 ? WindowAnimation_taskOpenEnterAnimation 724 : WindowAnimation_taskOpenExitAnimation; 725 break; 726 case TRANSIT_TASK_CLOSE: 727 animAttr = enter 728 ? WindowAnimation_taskCloseEnterAnimation 729 : WindowAnimation_taskCloseExitAnimation; 730 break; 731 case TRANSIT_TASK_TO_FRONT: 732 animAttr = enter 733 ? WindowAnimation_taskToFrontEnterAnimation 734 : WindowAnimation_taskToFrontExitAnimation; 735 break; 736 case TRANSIT_TASK_TO_BACK: 737 animAttr = enter 738 ? WindowAnimation_taskToBackEnterAnimation 739 : WindowAnimation_taskToBackExitAnimation; 740 break; 741 case TRANSIT_WALLPAPER_OPEN: 742 animAttr = enter 743 ? WindowAnimation_wallpaperOpenEnterAnimation 744 : WindowAnimation_wallpaperOpenExitAnimation; 745 break; 746 case TRANSIT_WALLPAPER_CLOSE: 747 animAttr = enter 748 ? WindowAnimation_wallpaperCloseEnterAnimation 749 : WindowAnimation_wallpaperCloseExitAnimation; 750 break; 751 case TRANSIT_WALLPAPER_INTRA_OPEN: 752 animAttr = enter 753 ? WindowAnimation_wallpaperIntraOpenEnterAnimation 754 : WindowAnimation_wallpaperIntraOpenExitAnimation; 755 break; 756 case TRANSIT_WALLPAPER_INTRA_CLOSE: 757 animAttr = enter 758 ? WindowAnimation_wallpaperIntraCloseEnterAnimation 759 : WindowAnimation_wallpaperIntraCloseExitAnimation; 760 break; 761 } 762 a = animAttr != 0 ? loadAnimation(lp, animAttr) : null; 763 if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, 764 "applyAnimation:" 765 + " anim=" + a 766 + " animAttr=0x" + Integer.toHexString(animAttr) 767 + " transit=" + transit + " isEntrance=" + enter 768 + " Callers=" + Debug.getCallers(3)); 769 } 770 return a; 771 } 772 773 void postAnimationCallback() { 774 if (mNextAppTransitionCallback != null) { 775 mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback)); 776 mNextAppTransitionCallback = null; 777 } 778 } 779 780 void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim, 781 IRemoteCallback startedCallback) { 782 if (isTransitionSet()) { 783 mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; 784 mNextAppTransitionPackage = packageName; 785 mNextAppTransitionThumbnail = null; 786 mNextAppTransitionEnter = enterAnim; 787 mNextAppTransitionExit = exitAnim; 788 postAnimationCallback(); 789 mNextAppTransitionCallback = startedCallback; 790 } else { 791 postAnimationCallback(); 792 } 793 } 794 795 void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth, 796 int startHeight) { 797 if (isTransitionSet()) { 798 mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP; 799 mNextAppTransitionPackage = null; 800 mNextAppTransitionThumbnail = null; 801 mNextAppTransitionStartX = startX; 802 mNextAppTransitionStartY = startY; 803 mNextAppTransitionStartWidth = startWidth; 804 mNextAppTransitionStartHeight = startHeight; 805 postAnimationCallback(); 806 mNextAppTransitionCallback = null; 807 } 808 } 809 810 void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY, 811 IRemoteCallback startedCallback, boolean scaleUp) { 812 if (isTransitionSet()) { 813 mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP 814 : NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN; 815 mNextAppTransitionPackage = null; 816 mNextAppTransitionThumbnail = srcThumb; 817 mNextAppTransitionScaleUp = scaleUp; 818 mNextAppTransitionStartX = startX; 819 mNextAppTransitionStartY = startY; 820 postAnimationCallback(); 821 mNextAppTransitionCallback = startedCallback; 822 } else { 823 postAnimationCallback(); 824 } 825 } 826 827 @Override 828 public String toString() { 829 return "mNextAppTransition=0x" + Integer.toHexString(mNextAppTransition); 830 } 831 832 /** 833 * Returns the human readable name of a window transition. 834 * 835 * @param transition The window transition. 836 * @return The transition symbolic name. 837 */ 838 public static String appTransitionToString(int transition) { 839 switch (transition) { 840 case TRANSIT_UNSET: { 841 return "TRANSIT_UNSET"; 842 } 843 case TRANSIT_NONE: { 844 return "TRANSIT_NONE"; 845 } 846 case TRANSIT_EXIT_MASK: { 847 return "TRANSIT_EXIT_MASK"; 848 } 849 case TRANSIT_ACTIVITY_OPEN: { 850 return "TRANSIT_ACTIVITY_OPEN"; 851 } 852 case TRANSIT_ACTIVITY_CLOSE: { 853 return "TRANSIT_ACTIVITY_CLOSE"; 854 } 855 case TRANSIT_TASK_OPEN: { 856 return "TRANSIT_TASK_OPEN"; 857 } 858 case TRANSIT_TASK_CLOSE: { 859 return "TRANSIT_TASK_CLOSE"; 860 } 861 case TRANSIT_TASK_TO_FRONT: { 862 return "TRANSIT_TASK_TO_FRONT"; 863 } 864 case TRANSIT_TASK_TO_BACK: { 865 return "TRANSIT_TASK_TO_BACK"; 866 } 867 case TRANSIT_WALLPAPER_CLOSE: { 868 return "TRANSIT_WALLPAPER_CLOSE"; 869 } 870 case TRANSIT_WALLPAPER_OPEN: { 871 return "TRANSIT_WALLPAPER_OPEN"; 872 } 873 case TRANSIT_WALLPAPER_INTRA_OPEN: { 874 return "TRANSIT_WALLPAPER_INTRA_OPEN"; 875 } 876 case TRANSIT_WALLPAPER_INTRA_CLOSE: { 877 return "TRANSIT_WALLPAPER_INTRA_CLOSE"; 878 } 879 default: { 880 return "<UNKNOWN>"; 881 } 882 } 883 } 884 885 private String appStateToString() { 886 switch (mAppTransitionState) { 887 case APP_STATE_IDLE: 888 return "APP_STATE_IDLE"; 889 case APP_STATE_READY: 890 return "APP_STATE_READY"; 891 case APP_STATE_RUNNING: 892 return "APP_STATE_RUNNING"; 893 case APP_STATE_TIMEOUT: 894 return "APP_STATE_TIMEOUT"; 895 default: 896 return "unknown state=" + mAppTransitionState; 897 } 898 } 899 900 private String transitTypeToString() { 901 switch (mNextAppTransitionType) { 902 case NEXT_TRANSIT_TYPE_NONE: 903 return "NEXT_TRANSIT_TYPE_NONE"; 904 case NEXT_TRANSIT_TYPE_CUSTOM: 905 return "NEXT_TRANSIT_TYPE_CUSTOM"; 906 case NEXT_TRANSIT_TYPE_SCALE_UP: 907 return "NEXT_TRANSIT_TYPE_SCALE_UP"; 908 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 909 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP"; 910 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 911 return "NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN"; 912 default: 913 return "unknown type=" + mNextAppTransitionType; 914 } 915 } 916 917 @Override 918 public void dump(PrintWriter pw) { 919 pw.print(" " + this); 920 pw.print(" mAppTransitionState="); pw.println(appStateToString()); 921 if (mNextAppTransitionType != NEXT_TRANSIT_TYPE_NONE) { 922 pw.print(" mNextAppTransitionType="); pw.println(transitTypeToString()); 923 } 924 switch (mNextAppTransitionType) { 925 case NEXT_TRANSIT_TYPE_CUSTOM: 926 pw.print(" mNextAppTransitionPackage="); 927 pw.println(mNextAppTransitionPackage); 928 pw.print(" mNextAppTransitionEnter=0x"); 929 pw.print(Integer.toHexString(mNextAppTransitionEnter)); 930 pw.print(" mNextAppTransitionExit=0x"); 931 pw.println(Integer.toHexString(mNextAppTransitionExit)); 932 break; 933 case NEXT_TRANSIT_TYPE_SCALE_UP: 934 pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX); 935 pw.print(" mNextAppTransitionStartY="); 936 pw.println(mNextAppTransitionStartY); 937 pw.print(" mNextAppTransitionStartWidth="); 938 pw.print(mNextAppTransitionStartWidth); 939 pw.print(" mNextAppTransitionStartHeight="); 940 pw.println(mNextAppTransitionStartHeight); 941 break; 942 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP: 943 case NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN: 944 pw.print(" mNextAppTransitionThumbnail="); 945 pw.print(mNextAppTransitionThumbnail); 946 pw.print(" mNextAppTransitionStartX="); 947 pw.print(mNextAppTransitionStartX); 948 pw.print(" mNextAppTransitionStartY="); 949 pw.println(mNextAppTransitionStartY); 950 pw.print(" mNextAppTransitionScaleUp="); pw.println(mNextAppTransitionScaleUp); 951 break; 952 } 953 if (mNextAppTransitionCallback != null) { 954 pw.print(" mNextAppTransitionCallback="); 955 pw.println(mNextAppTransitionCallback); 956 } 957 } 958 959 public void setCurrentUser(int newUserId) { 960 mCurrentUserId = newUserId; 961 } 962} 963