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