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