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