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