1 /* 2 * Copyright (C) 2010 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 17 package com.android.server.wm; 18 19 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 20 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; 21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 24 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; 25 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER; 26 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING; 27 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED; 28 29 import android.content.Context; 30 import android.graphics.GraphicBuffer; 31 import android.graphics.Matrix; 32 import android.graphics.Rect; 33 import android.os.IBinder; 34 import android.util.Slog; 35 import android.util.proto.ProtoOutputStream; 36 import android.view.Display; 37 import android.view.DisplayInfo; 38 import android.view.Surface; 39 import android.view.Surface.OutOfResourcesException; 40 import android.view.SurfaceControl; 41 import android.view.SurfaceControl.Transaction; 42 import android.view.SurfaceSession; 43 import android.view.animation.Animation; 44 import android.view.animation.AnimationUtils; 45 import android.view.animation.Transformation; 46 47 import java.io.PrintWriter; 48 49 class ScreenRotationAnimation { 50 static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM; 51 static final boolean DEBUG_STATE = false; 52 static final boolean DEBUG_TRANSFORMS = false; 53 static final boolean TWO_PHASE_ANIMATION = false; 54 static final boolean USE_CUSTOM_BLACK_FRAME = false; 55 56 /* 57 * Layers for screen rotation animation. We put these layers above 58 * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows. 59 */ 60 static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; 61 static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE; 62 static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1; 63 static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2; 64 static final int SCREEN_FREEZE_LAYER_CUSTOM = SCREEN_FREEZE_LAYER_BASE + 3; 65 66 final Context mContext; 67 final DisplayContent mDisplayContent; 68 SurfaceControl mSurfaceControl; 69 BlackFrame mCustomBlackFrame; 70 BlackFrame mExitingBlackFrame; 71 BlackFrame mEnteringBlackFrame; 72 int mWidth, mHeight; 73 74 int mOriginalRotation; 75 int mOriginalWidth, mOriginalHeight; 76 int mCurRotation; 77 Rect mOriginalDisplayRect = new Rect(); 78 Rect mCurrentDisplayRect = new Rect(); 79 80 // For all animations, "exit" is for the UI elements that are going 81 // away (that is the snapshot of the old screen), and "enter" is for 82 // the new UI elements that are appearing (that is the active windows 83 // in their final orientation). 84 85 // The starting animation for the exiting and entering elements. This 86 // animation applies a transformation while the rotation is in progress. 87 // It is started immediately, before the new entering UI is ready. 88 Animation mStartExitAnimation; 89 final Transformation mStartExitTransformation = new Transformation(); 90 Animation mStartEnterAnimation; 91 final Transformation mStartEnterTransformation = new Transformation(); 92 Animation mStartFrameAnimation; 93 final Transformation mStartFrameTransformation = new Transformation(); 94 95 // The finishing animation for the exiting and entering elements. This 96 // animation needs to undo the transformation of the starting animation. 97 // It starts running once the new rotation UI elements are ready to be 98 // displayed. 99 Animation mFinishExitAnimation; 100 final Transformation mFinishExitTransformation = new Transformation(); 101 Animation mFinishEnterAnimation; 102 final Transformation mFinishEnterTransformation = new Transformation(); 103 Animation mFinishFrameAnimation; 104 final Transformation mFinishFrameTransformation = new Transformation(); 105 106 // The current active animation to move from the old to the new rotated 107 // state. Which animation is run here will depend on the old and new 108 // rotations. 109 Animation mRotateExitAnimation; 110 final Transformation mRotateExitTransformation = new Transformation(); 111 Animation mRotateEnterAnimation; 112 final Transformation mRotateEnterTransformation = new Transformation(); 113 Animation mRotateFrameAnimation; 114 final Transformation mRotateFrameTransformation = new Transformation(); 115 116 // A previously running rotate animation. This will be used if we need 117 // to switch to a new rotation before finishing the previous one. 118 Animation mLastRotateExitAnimation; 119 final Transformation mLastRotateExitTransformation = new Transformation(); 120 Animation mLastRotateEnterAnimation; 121 final Transformation mLastRotateEnterTransformation = new Transformation(); 122 Animation mLastRotateFrameAnimation; 123 final Transformation mLastRotateFrameTransformation = new Transformation(); 124 125 // Complete transformations being applied. 126 final Transformation mExitTransformation = new Transformation(); 127 final Transformation mEnterTransformation = new Transformation(); 128 final Transformation mFrameTransformation = new Transformation(); 129 130 boolean mStarted; 131 boolean mAnimRunning; 132 boolean mFinishAnimReady; 133 long mFinishAnimStartTime; 134 boolean mForceDefaultOrientation; 135 136 final Matrix mFrameInitialMatrix = new Matrix(); 137 final Matrix mSnapshotInitialMatrix = new Matrix(); 138 final Matrix mSnapshotFinalMatrix = new Matrix(); 139 final Matrix mExitFrameFinalMatrix = new Matrix(); 140 final Matrix mTmpMatrix = new Matrix(); 141 final float[] mTmpFloats = new float[9]; 142 private boolean mMoreRotateEnter; 143 private boolean mMoreRotateExit; 144 private boolean mMoreRotateFrame; 145 private boolean mMoreFinishEnter; 146 private boolean mMoreFinishExit; 147 private boolean mMoreFinishFrame; 148 private boolean mMoreStartEnter; 149 private boolean mMoreStartExit; 150 private boolean mMoreStartFrame; 151 long mHalfwayPoint; 152 153 private final WindowManagerService mService; 154 printTo(String prefix, PrintWriter pw)155 public void printTo(String prefix, PrintWriter pw) { 156 pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl); 157 pw.print(" mWidth="); pw.print(mWidth); 158 pw.print(" mHeight="); pw.println(mHeight); 159 if (USE_CUSTOM_BLACK_FRAME) { 160 pw.print(prefix); pw.print("mCustomBlackFrame="); pw.println(mCustomBlackFrame); 161 if (mCustomBlackFrame != null) { 162 mCustomBlackFrame.printTo(prefix + " ", pw); 163 } 164 } 165 pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame); 166 if (mExitingBlackFrame != null) { 167 mExitingBlackFrame.printTo(prefix + " ", pw); 168 } 169 pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame); 170 if (mEnteringBlackFrame != null) { 171 mEnteringBlackFrame.printTo(prefix + " ", pw); 172 } 173 pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation); 174 pw.print(" mOriginalRotation="); pw.println(mOriginalRotation); 175 pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth); 176 pw.print(" mOriginalHeight="); pw.println(mOriginalHeight); 177 pw.print(prefix); pw.print("mStarted="); pw.print(mStarted); 178 pw.print(" mAnimRunning="); pw.print(mAnimRunning); 179 pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady); 180 pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime); 181 pw.print(prefix); pw.print("mStartExitAnimation="); pw.print(mStartExitAnimation); 182 pw.print(" "); mStartExitTransformation.printShortString(pw); pw.println(); 183 pw.print(prefix); pw.print("mStartEnterAnimation="); pw.print(mStartEnterAnimation); 184 pw.print(" "); mStartEnterTransformation.printShortString(pw); pw.println(); 185 pw.print(prefix); pw.print("mStartFrameAnimation="); pw.print(mStartFrameAnimation); 186 pw.print(" "); mStartFrameTransformation.printShortString(pw); pw.println(); 187 pw.print(prefix); pw.print("mFinishExitAnimation="); pw.print(mFinishExitAnimation); 188 pw.print(" "); mFinishExitTransformation.printShortString(pw); pw.println(); 189 pw.print(prefix); pw.print("mFinishEnterAnimation="); pw.print(mFinishEnterAnimation); 190 pw.print(" "); mFinishEnterTransformation.printShortString(pw); pw.println(); 191 pw.print(prefix); pw.print("mFinishFrameAnimation="); pw.print(mFinishFrameAnimation); 192 pw.print(" "); mFinishFrameTransformation.printShortString(pw); pw.println(); 193 pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation); 194 pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println(); 195 pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation); 196 pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println(); 197 pw.print(prefix); pw.print("mRotateFrameAnimation="); pw.print(mRotateFrameAnimation); 198 pw.print(" "); mRotateFrameTransformation.printShortString(pw); pw.println(); 199 pw.print(prefix); pw.print("mExitTransformation="); 200 mExitTransformation.printShortString(pw); pw.println(); 201 pw.print(prefix); pw.print("mEnterTransformation="); 202 mEnterTransformation.printShortString(pw); pw.println(); 203 pw.print(prefix); pw.print("mFrameTransformation="); 204 mFrameTransformation.printShortString(pw); pw.println(); 205 pw.print(prefix); pw.print("mFrameInitialMatrix="); 206 mFrameInitialMatrix.printShortString(pw); 207 pw.println(); 208 pw.print(prefix); pw.print("mSnapshotInitialMatrix="); 209 mSnapshotInitialMatrix.printShortString(pw); 210 pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw); 211 pw.println(); 212 pw.print(prefix); pw.print("mExitFrameFinalMatrix="); 213 mExitFrameFinalMatrix.printShortString(pw); 214 pw.println(); 215 pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation); 216 if (mForceDefaultOrientation) { 217 pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString()); 218 pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString()); 219 } 220 } 221 writeToProto(ProtoOutputStream proto, long fieldId)222 public void writeToProto(ProtoOutputStream proto, long fieldId) { 223 final long token = proto.start(fieldId); 224 proto.write(STARTED, mStarted); 225 proto.write(ANIMATION_RUNNING, mAnimRunning); 226 proto.end(token); 227 } 228 ScreenRotationAnimation(Context context, DisplayContent displayContent, boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service)229 public ScreenRotationAnimation(Context context, DisplayContent displayContent, 230 boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) { 231 mService = service; 232 mContext = context; 233 mDisplayContent = displayContent; 234 displayContent.getBounds(mOriginalDisplayRect); 235 236 // Screenshot does NOT include rotation! 237 final Display display = displayContent.getDisplay(); 238 int originalRotation = display.getRotation(); 239 final int originalWidth; 240 final int originalHeight; 241 DisplayInfo displayInfo = displayContent.getDisplayInfo(); 242 if (forceDefaultOrientation) { 243 // Emulated orientation. 244 mForceDefaultOrientation = true; 245 originalWidth = displayContent.mBaseDisplayWidth; 246 originalHeight = displayContent.mBaseDisplayHeight; 247 } else { 248 // Normal situation 249 originalWidth = displayInfo.logicalWidth; 250 originalHeight = displayInfo.logicalHeight; 251 } 252 if (originalRotation == Surface.ROTATION_90 253 || originalRotation == Surface.ROTATION_270) { 254 mWidth = originalHeight; 255 mHeight = originalWidth; 256 } else { 257 mWidth = originalWidth; 258 mHeight = originalHeight; 259 } 260 261 mOriginalRotation = originalRotation; 262 mOriginalWidth = originalWidth; 263 mOriginalHeight = originalHeight; 264 265 final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 266 try { 267 mSurfaceControl = displayContent.makeOverlay() 268 .setName("ScreenshotSurface") 269 .setSize(mWidth, mHeight) 270 .setSecure(isSecure) 271 .build(); 272 273 // In case display bounds change, screenshot buffer and surface may mismatch so set a 274 // scaling mode. 275 Transaction t2 = new Transaction(); 276 t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW); 277 t2.apply(true /* sync */); 278 279 // capture a screenshot into the surface we just created 280 // TODO(multidisplay): we should use the proper display 281 final int displayId = SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN; 282 final IBinder displayHandle = SurfaceControl.getBuiltInDisplay(displayId); 283 // This null check below is to guard a race condition where WMS didn't have a chance to 284 // respond to display disconnection before handling rotation , that surfaceflinger may 285 // return a null handle here because it doesn't think that display is valid anymore. 286 if (displayHandle != null) { 287 Surface sur = new Surface(); 288 sur.copyFrom(mSurfaceControl); 289 GraphicBuffer gb = SurfaceControl.screenshotToBufferWithSecureLayersUnsafe( 290 new Rect(), 0 /* width */, 0 /* height */, 0 /* minLayer */, 291 0 /* maxLayer */, false /* useIdentityTransform */, 0 /* rotation */); 292 if (gb != null) { 293 try { 294 sur.attachAndQueueBuffer(gb); 295 } catch (RuntimeException e) { 296 Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage()); 297 } 298 // If the screenshot contains secure layers, we have to make sure the 299 // screenshot surface we display it in also has FLAG_SECURE so that 300 // the user can not screenshot secure layers via the screenshot surface. 301 if (gb.doesContainSecureLayers()) { 302 t.setSecure(mSurfaceControl, true); 303 } 304 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT); 305 t.setAlpha(mSurfaceControl, 0); 306 t.show(mSurfaceControl); 307 } else { 308 Slog.w(TAG, "Unable to take screenshot of display " + displayId); 309 } 310 sur.destroy(); 311 } else { 312 Slog.w(TAG, "Built-in display " + displayId + " is null."); 313 } 314 } catch (OutOfResourcesException e) { 315 Slog.w(TAG, "Unable to allocate freeze surface", e); 316 } 317 318 if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM, 319 " FREEZE " + mSurfaceControl + ": CREATE"); 320 setRotation(t, originalRotation); 321 t.apply(); 322 } 323 hasScreenshot()324 boolean hasScreenshot() { 325 return mSurfaceControl != null; 326 } 327 setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha)328 private void setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha) { 329 if (mSurfaceControl != null) { 330 matrix.getValues(mTmpFloats); 331 float x = mTmpFloats[Matrix.MTRANS_X]; 332 float y = mTmpFloats[Matrix.MTRANS_Y]; 333 if (mForceDefaultOrientation) { 334 mDisplayContent.getBounds(mCurrentDisplayRect); 335 x -= mCurrentDisplayRect.left; 336 y -= mCurrentDisplayRect.top; 337 } 338 t.setPosition(mSurfaceControl, x, y); 339 t.setMatrix(mSurfaceControl, 340 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y], 341 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]); 342 t.setAlpha(mSurfaceControl, alpha); 343 if (DEBUG_TRANSFORMS) { 344 float[] srcPnts = new float[] { 0, 0, mWidth, mHeight }; 345 float[] dstPnts = new float[4]; 346 matrix.mapPoints(dstPnts, srcPnts); 347 Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1] 348 + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")"); 349 Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1] 350 + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")"); 351 } 352 } 353 } 354 createRotationMatrix(int rotation, int width, int height, Matrix outMatrix)355 public static void createRotationMatrix(int rotation, int width, int height, 356 Matrix outMatrix) { 357 switch (rotation) { 358 case Surface.ROTATION_0: 359 outMatrix.reset(); 360 break; 361 case Surface.ROTATION_90: 362 outMatrix.setRotate(90, 0, 0); 363 outMatrix.postTranslate(height, 0); 364 break; 365 case Surface.ROTATION_180: 366 outMatrix.setRotate(180, 0, 0); 367 outMatrix.postTranslate(width, height); 368 break; 369 case Surface.ROTATION_270: 370 outMatrix.setRotate(270, 0, 0); 371 outMatrix.postTranslate(0, width); 372 break; 373 } 374 } 375 setRotation(SurfaceControl.Transaction t, int rotation)376 private void setRotation(SurfaceControl.Transaction t, int rotation) { 377 mCurRotation = rotation; 378 379 // Compute the transformation matrix that must be applied 380 // to the snapshot to make it stay in the same original position 381 // with the current screen rotation. 382 int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0); 383 createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); 384 385 if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta); 386 setSnapshotTransform(t, mSnapshotInitialMatrix, 1.0f); 387 } 388 setRotation(SurfaceControl.Transaction t, int rotation, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight)389 public boolean setRotation(SurfaceControl.Transaction t, int rotation, 390 long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) { 391 setRotation(t, rotation); 392 if (TWO_PHASE_ANIMATION) { 393 return startAnimation(t, maxAnimationDuration, animationScale, 394 finalWidth, finalHeight, false, 0, 0); 395 } 396 397 // Don't start animation yet. 398 return false; 399 } 400 401 /** 402 * Returns true if animating. 403 */ startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, boolean dismissing, int exitAnim, int enterAnim)404 private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, 405 float animationScale, int finalWidth, int finalHeight, boolean dismissing, 406 int exitAnim, int enterAnim) { 407 if (mSurfaceControl == null) { 408 // Can't do animation. 409 return false; 410 } 411 if (mStarted) { 412 return true; 413 } 414 415 mStarted = true; 416 417 boolean firstStart = false; 418 419 // Figure out how the screen has moved from the original rotation. 420 int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation); 421 422 if (TWO_PHASE_ANIMATION && mFinishExitAnimation == null 423 && (!dismissing || delta != Surface.ROTATION_0)) { 424 if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations"); 425 firstStart = true; 426 mStartExitAnimation = AnimationUtils.loadAnimation(mContext, 427 com.android.internal.R.anim.screen_rotate_start_exit); 428 mStartEnterAnimation = AnimationUtils.loadAnimation(mContext, 429 com.android.internal.R.anim.screen_rotate_start_enter); 430 if (USE_CUSTOM_BLACK_FRAME) { 431 mStartFrameAnimation = AnimationUtils.loadAnimation(mContext, 432 com.android.internal.R.anim.screen_rotate_start_frame); 433 } 434 mFinishExitAnimation = AnimationUtils.loadAnimation(mContext, 435 com.android.internal.R.anim.screen_rotate_finish_exit); 436 mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext, 437 com.android.internal.R.anim.screen_rotate_finish_enter); 438 if (USE_CUSTOM_BLACK_FRAME) { 439 mFinishFrameAnimation = AnimationUtils.loadAnimation(mContext, 440 com.android.internal.R.anim.screen_rotate_finish_frame); 441 } 442 } 443 444 if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth=" 445 + finalWidth + " finalHeight=" + finalHeight 446 + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight); 447 448 final boolean customAnim; 449 if (exitAnim != 0 && enterAnim != 0) { 450 customAnim = true; 451 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim); 452 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim); 453 } else { 454 customAnim = false; 455 switch (delta) { 456 case Surface.ROTATION_0: 457 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 458 com.android.internal.R.anim.screen_rotate_0_exit); 459 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 460 com.android.internal.R.anim.screen_rotate_0_enter); 461 if (USE_CUSTOM_BLACK_FRAME) { 462 mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext, 463 com.android.internal.R.anim.screen_rotate_0_frame); 464 } 465 break; 466 case Surface.ROTATION_90: 467 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 468 com.android.internal.R.anim.screen_rotate_plus_90_exit); 469 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 470 com.android.internal.R.anim.screen_rotate_plus_90_enter); 471 if (USE_CUSTOM_BLACK_FRAME) { 472 mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext, 473 com.android.internal.R.anim.screen_rotate_plus_90_frame); 474 } 475 break; 476 case Surface.ROTATION_180: 477 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 478 com.android.internal.R.anim.screen_rotate_180_exit); 479 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 480 com.android.internal.R.anim.screen_rotate_180_enter); 481 if (USE_CUSTOM_BLACK_FRAME) { 482 mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext, 483 com.android.internal.R.anim.screen_rotate_180_frame); 484 } 485 break; 486 case Surface.ROTATION_270: 487 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 488 com.android.internal.R.anim.screen_rotate_minus_90_exit); 489 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 490 com.android.internal.R.anim.screen_rotate_minus_90_enter); 491 if (USE_CUSTOM_BLACK_FRAME) { 492 mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext, 493 com.android.internal.R.anim.screen_rotate_minus_90_frame); 494 } 495 break; 496 } 497 } 498 499 // Initialize the animations. This is a hack, redefining what "parent" 500 // means to allow supplying the last and next size. In this definition 501 // "%p" is the original (let's call it "previous") size, and "%" is the 502 // screen's current/new size. 503 if (TWO_PHASE_ANIMATION && firstStart) { 504 // Compute partial steps between original and final sizes. These 505 // are used for the dimensions of the exiting and entering elements, 506 // so they are never stretched too significantly. 507 final int halfWidth = (finalWidth + mOriginalWidth) / 2; 508 final int halfHeight = (finalHeight + mOriginalHeight) / 2; 509 510 if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations"); 511 mStartEnterAnimation.initialize(finalWidth, finalHeight, 512 halfWidth, halfHeight); 513 mStartExitAnimation.initialize(halfWidth, halfHeight, 514 mOriginalWidth, mOriginalHeight); 515 mFinishEnterAnimation.initialize(finalWidth, finalHeight, 516 halfWidth, halfHeight); 517 mFinishExitAnimation.initialize(halfWidth, halfHeight, 518 mOriginalWidth, mOriginalHeight); 519 if (USE_CUSTOM_BLACK_FRAME) { 520 mStartFrameAnimation.initialize(finalWidth, finalHeight, 521 mOriginalWidth, mOriginalHeight); 522 mFinishFrameAnimation.initialize(finalWidth, finalHeight, 523 mOriginalWidth, mOriginalHeight); 524 } 525 } 526 mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 527 mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 528 if (USE_CUSTOM_BLACK_FRAME) { 529 mRotateFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, 530 mOriginalHeight); 531 } 532 mAnimRunning = false; 533 mFinishAnimReady = false; 534 mFinishAnimStartTime = -1; 535 536 if (TWO_PHASE_ANIMATION && firstStart) { 537 mStartExitAnimation.restrictDuration(maxAnimationDuration); 538 mStartExitAnimation.scaleCurrentDuration(animationScale); 539 mStartEnterAnimation.restrictDuration(maxAnimationDuration); 540 mStartEnterAnimation.scaleCurrentDuration(animationScale); 541 mFinishExitAnimation.restrictDuration(maxAnimationDuration); 542 mFinishExitAnimation.scaleCurrentDuration(animationScale); 543 mFinishEnterAnimation.restrictDuration(maxAnimationDuration); 544 mFinishEnterAnimation.scaleCurrentDuration(animationScale); 545 if (USE_CUSTOM_BLACK_FRAME) { 546 mStartFrameAnimation.restrictDuration(maxAnimationDuration); 547 mStartFrameAnimation.scaleCurrentDuration(animationScale); 548 mFinishFrameAnimation.restrictDuration(maxAnimationDuration); 549 mFinishFrameAnimation.scaleCurrentDuration(animationScale); 550 } 551 } 552 mRotateExitAnimation.restrictDuration(maxAnimationDuration); 553 mRotateExitAnimation.scaleCurrentDuration(animationScale); 554 mRotateEnterAnimation.restrictDuration(maxAnimationDuration); 555 mRotateEnterAnimation.scaleCurrentDuration(animationScale); 556 if (USE_CUSTOM_BLACK_FRAME) { 557 mRotateFrameAnimation.restrictDuration(maxAnimationDuration); 558 mRotateFrameAnimation.scaleCurrentDuration(animationScale); 559 } 560 561 final int layerStack = mDisplayContent.getDisplay().getLayerStack(); 562 if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) { 563 // Compute the transformation matrix that must be applied 564 // the the black frame to make it stay in the initial position 565 // before the new screen rotation. This is different than the 566 // snapshot transformation because the snapshot is always based 567 // of the native orientation of the screen, not the orientation 568 // we were last in. 569 createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix); 570 571 try { 572 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1, 573 mOriginalWidth*2, mOriginalHeight*2); 574 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight); 575 mCustomBlackFrame = new BlackFrame(t, outer, inner, 576 SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false); 577 mCustomBlackFrame.setMatrix(t, mFrameInitialMatrix); 578 } catch (OutOfResourcesException e) { 579 Slog.w(TAG, "Unable to allocate black surface", e); 580 } 581 } 582 583 if (!customAnim && mExitingBlackFrame == null) { 584 try { 585 // Compute the transformation matrix that must be applied 586 // the the black frame to make it stay in the initial position 587 // before the new screen rotation. This is different than the 588 // snapshot transformation because the snapshot is always based 589 // of the native orientation of the screen, not the orientation 590 // we were last in. 591 createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix); 592 593 final Rect outer; 594 final Rect inner; 595 if (mForceDefaultOrientation) { 596 // Going from a smaller Display to a larger Display, add curtains to sides 597 // or top and bottom. Going from a larger to smaller display will result in 598 // no BlackSurfaces being constructed. 599 outer = mCurrentDisplayRect; 600 inner = mOriginalDisplayRect; 601 } else { 602 outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1, 603 mOriginalWidth*2, mOriginalHeight*2); 604 inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight); 605 } 606 mExitingBlackFrame = new BlackFrame(t, outer, inner, 607 SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation); 608 mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix); 609 } catch (OutOfResourcesException e) { 610 Slog.w(TAG, "Unable to allocate black surface", e); 611 } 612 } 613 614 if (customAnim && mEnteringBlackFrame == null) { 615 try { 616 Rect outer = new Rect(-finalWidth*1, -finalHeight*1, 617 finalWidth*2, finalHeight*2); 618 Rect inner = new Rect(0, 0, finalWidth, finalHeight); 619 mEnteringBlackFrame = new BlackFrame(t, outer, inner, 620 SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false); 621 } catch (OutOfResourcesException e) { 622 Slog.w(TAG, "Unable to allocate black surface", e); 623 } 624 } 625 626 return true; 627 } 628 629 /** 630 * Returns true if animating. 631 */ dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)632 public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, 633 float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { 634 if (DEBUG_STATE) Slog.v(TAG, "Dismiss!"); 635 if (mSurfaceControl == null) { 636 // Can't do animation. 637 return false; 638 } 639 if (!mStarted) { 640 startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, 641 true, exitAnim, enterAnim); 642 } 643 if (!mStarted) { 644 return false; 645 } 646 if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true"); 647 mFinishAnimReady = true; 648 return true; 649 } 650 kill()651 public void kill() { 652 if (DEBUG_STATE) Slog.v(TAG, "Kill!"); 653 if (mSurfaceControl != null) { 654 if (SHOW_TRANSACTIONS || 655 SHOW_SURFACE_ALLOC) Slog.i(TAG_WM, 656 " FREEZE " + mSurfaceControl + ": DESTROY"); 657 mSurfaceControl.destroy(); 658 mSurfaceControl = null; 659 } 660 if (mCustomBlackFrame != null) { 661 mCustomBlackFrame.kill(); 662 mCustomBlackFrame = null; 663 } 664 if (mExitingBlackFrame != null) { 665 mExitingBlackFrame.kill(); 666 mExitingBlackFrame = null; 667 } 668 if (mEnteringBlackFrame != null) { 669 mEnteringBlackFrame.kill(); 670 mEnteringBlackFrame = null; 671 } 672 if (TWO_PHASE_ANIMATION) { 673 if (mStartExitAnimation != null) { 674 mStartExitAnimation.cancel(); 675 mStartExitAnimation = null; 676 } 677 if (mStartEnterAnimation != null) { 678 mStartEnterAnimation.cancel(); 679 mStartEnterAnimation = null; 680 } 681 if (mFinishExitAnimation != null) { 682 mFinishExitAnimation.cancel(); 683 mFinishExitAnimation = null; 684 } 685 if (mFinishEnterAnimation != null) { 686 mFinishEnterAnimation.cancel(); 687 mFinishEnterAnimation = null; 688 } 689 } 690 if (USE_CUSTOM_BLACK_FRAME) { 691 if (mStartFrameAnimation != null) { 692 mStartFrameAnimation.cancel(); 693 mStartFrameAnimation = null; 694 } 695 if (mRotateFrameAnimation != null) { 696 mRotateFrameAnimation.cancel(); 697 mRotateFrameAnimation = null; 698 } 699 if (mFinishFrameAnimation != null) { 700 mFinishFrameAnimation.cancel(); 701 mFinishFrameAnimation = null; 702 } 703 } 704 if (mRotateExitAnimation != null) { 705 mRotateExitAnimation.cancel(); 706 mRotateExitAnimation = null; 707 } 708 if (mRotateEnterAnimation != null) { 709 mRotateEnterAnimation.cancel(); 710 mRotateEnterAnimation = null; 711 } 712 } 713 isAnimating()714 public boolean isAnimating() { 715 return hasAnimations() || (TWO_PHASE_ANIMATION && mFinishAnimReady); 716 } 717 isRotating()718 public boolean isRotating() { 719 return mCurRotation != mOriginalRotation; 720 } 721 hasAnimations()722 private boolean hasAnimations() { 723 return (TWO_PHASE_ANIMATION && 724 (mStartEnterAnimation != null || mStartExitAnimation != null 725 || mFinishEnterAnimation != null || mFinishExitAnimation != null)) 726 || (USE_CUSTOM_BLACK_FRAME && 727 (mStartFrameAnimation != null || mRotateFrameAnimation != null 728 || mFinishFrameAnimation != null)) 729 || mRotateEnterAnimation != null || mRotateExitAnimation != null; 730 } 731 stepAnimation(long now)732 private boolean stepAnimation(long now) { 733 if (now > mHalfwayPoint) { 734 mHalfwayPoint = Long.MAX_VALUE; 735 } 736 if (mFinishAnimReady && mFinishAnimStartTime < 0) { 737 if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready"); 738 mFinishAnimStartTime = now; 739 } 740 741 if (TWO_PHASE_ANIMATION) { 742 mMoreStartExit = false; 743 if (mStartExitAnimation != null) { 744 mMoreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation); 745 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation); 746 } 747 748 mMoreStartEnter = false; 749 if (mStartEnterAnimation != null) { 750 mMoreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation); 751 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation); 752 } 753 } 754 if (USE_CUSTOM_BLACK_FRAME) { 755 mMoreStartFrame = false; 756 if (mStartFrameAnimation != null) { 757 mMoreStartFrame = mStartFrameAnimation.getTransformation(now, mStartFrameTransformation); 758 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start frame: " + mStartFrameTransformation); 759 } 760 } 761 762 long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0; 763 if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow); 764 765 if (TWO_PHASE_ANIMATION) { 766 mMoreFinishExit = false; 767 if (mFinishExitAnimation != null) { 768 mMoreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation); 769 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation); 770 } 771 772 mMoreFinishEnter = false; 773 if (mFinishEnterAnimation != null) { 774 mMoreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation); 775 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation); 776 } 777 } 778 if (USE_CUSTOM_BLACK_FRAME) { 779 mMoreFinishFrame = false; 780 if (mFinishFrameAnimation != null) { 781 mMoreFinishFrame = mFinishFrameAnimation.getTransformation(finishNow, mFinishFrameTransformation); 782 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish frame: " + mFinishFrameTransformation); 783 } 784 } 785 786 mMoreRotateExit = false; 787 if (mRotateExitAnimation != null) { 788 mMoreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation); 789 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation); 790 } 791 792 mMoreRotateEnter = false; 793 if (mRotateEnterAnimation != null) { 794 mMoreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation); 795 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation); 796 } 797 798 if (USE_CUSTOM_BLACK_FRAME) { 799 mMoreRotateFrame = false; 800 if (mRotateFrameAnimation != null) { 801 mMoreRotateFrame = mRotateFrameAnimation.getTransformation(now, mRotateFrameTransformation); 802 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate frame: " + mRotateFrameTransformation); 803 } 804 } 805 806 if (!mMoreRotateExit && (!TWO_PHASE_ANIMATION || (!mMoreStartExit && !mMoreFinishExit))) { 807 if (TWO_PHASE_ANIMATION) { 808 if (mStartExitAnimation != null) { 809 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing start exit anim!"); 810 mStartExitAnimation.cancel(); 811 mStartExitAnimation = null; 812 mStartExitTransformation.clear(); 813 } 814 if (mFinishExitAnimation != null) { 815 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing finish exit anim!"); 816 mFinishExitAnimation.cancel(); 817 mFinishExitAnimation = null; 818 mFinishExitTransformation.clear(); 819 } 820 } 821 if (mRotateExitAnimation != null) { 822 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing rotate exit anim!"); 823 mRotateExitAnimation.cancel(); 824 mRotateExitAnimation = null; 825 mRotateExitTransformation.clear(); 826 } 827 } 828 829 if (!mMoreRotateEnter && (!TWO_PHASE_ANIMATION || (!mMoreStartEnter && !mMoreFinishEnter))) { 830 if (TWO_PHASE_ANIMATION) { 831 if (mStartEnterAnimation != null) { 832 if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing start enter anim!"); 833 mStartEnterAnimation.cancel(); 834 mStartEnterAnimation = null; 835 mStartEnterTransformation.clear(); 836 } 837 if (mFinishEnterAnimation != null) { 838 if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing finish enter anim!"); 839 mFinishEnterAnimation.cancel(); 840 mFinishEnterAnimation = null; 841 mFinishEnterTransformation.clear(); 842 } 843 } 844 if (mRotateEnterAnimation != null) { 845 if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing rotate enter anim!"); 846 mRotateEnterAnimation.cancel(); 847 mRotateEnterAnimation = null; 848 mRotateEnterTransformation.clear(); 849 } 850 } 851 852 if (USE_CUSTOM_BLACK_FRAME && !mMoreStartFrame && !mMoreRotateFrame && !mMoreFinishFrame) { 853 if (mStartFrameAnimation != null) { 854 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing start frame anim!"); 855 mStartFrameAnimation.cancel(); 856 mStartFrameAnimation = null; 857 mStartFrameTransformation.clear(); 858 } 859 if (mFinishFrameAnimation != null) { 860 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing finish frame anim!"); 861 mFinishFrameAnimation.cancel(); 862 mFinishFrameAnimation = null; 863 mFinishFrameTransformation.clear(); 864 } 865 if (mRotateFrameAnimation != null) { 866 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing rotate frame anim!"); 867 mRotateFrameAnimation.cancel(); 868 mRotateFrameAnimation = null; 869 mRotateFrameTransformation.clear(); 870 } 871 } 872 873 mExitTransformation.set(mRotateExitTransformation); 874 mEnterTransformation.set(mRotateEnterTransformation); 875 if (TWO_PHASE_ANIMATION) { 876 mExitTransformation.compose(mStartExitTransformation); 877 mExitTransformation.compose(mFinishExitTransformation); 878 879 mEnterTransformation.compose(mStartEnterTransformation); 880 mEnterTransformation.compose(mFinishEnterTransformation); 881 } 882 883 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation); 884 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation); 885 886 if (USE_CUSTOM_BLACK_FRAME) { 887 //mFrameTransformation.set(mRotateExitTransformation); 888 //mFrameTransformation.compose(mStartExitTransformation); 889 //mFrameTransformation.compose(mFinishExitTransformation); 890 mFrameTransformation.set(mRotateFrameTransformation); 891 mFrameTransformation.compose(mStartFrameTransformation); 892 mFrameTransformation.compose(mFinishFrameTransformation); 893 mFrameTransformation.getMatrix().preConcat(mFrameInitialMatrix); 894 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final frame: " + mFrameTransformation); 895 } 896 897 final boolean more = (TWO_PHASE_ANIMATION 898 && (mMoreStartEnter || mMoreStartExit || mMoreFinishEnter || mMoreFinishExit)) 899 || (USE_CUSTOM_BLACK_FRAME 900 && (mMoreStartFrame || mMoreRotateFrame || mMoreFinishFrame)) 901 || mMoreRotateEnter || mMoreRotateExit 902 || !mFinishAnimReady; 903 904 mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix); 905 906 if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more); 907 908 return more; 909 } 910 updateSurfaces(SurfaceControl.Transaction t)911 void updateSurfaces(SurfaceControl.Transaction t) { 912 if (!mStarted) { 913 return; 914 } 915 916 if (mSurfaceControl != null) { 917 if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) { 918 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface"); 919 t.hide(mSurfaceControl); 920 } 921 } 922 923 if (mCustomBlackFrame != null) { 924 if (!mMoreStartFrame && !mMoreFinishFrame && !mMoreRotateFrame) { 925 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding black frame"); 926 mCustomBlackFrame.hide(t); 927 } else { 928 mCustomBlackFrame.setMatrix(t, mFrameTransformation.getMatrix()); 929 } 930 } 931 932 if (mExitingBlackFrame != null) { 933 if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) { 934 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding exiting frame"); 935 mExitingBlackFrame.hide(t); 936 } else { 937 mExitFrameFinalMatrix.setConcat(mExitTransformation.getMatrix(), mFrameInitialMatrix); 938 mExitingBlackFrame.setMatrix(t, mExitFrameFinalMatrix); 939 if (mForceDefaultOrientation) { 940 mExitingBlackFrame.setAlpha(t, mExitTransformation.getAlpha()); 941 } 942 } 943 } 944 945 if (mEnteringBlackFrame != null) { 946 if (!mMoreStartEnter && !mMoreFinishEnter && !mMoreRotateEnter) { 947 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding entering frame"); 948 mEnteringBlackFrame.hide(t); 949 } else { 950 mEnteringBlackFrame.setMatrix(t, mEnterTransformation.getMatrix()); 951 } 952 } 953 954 setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha()); 955 } 956 stepAnimationLocked(long now)957 public boolean stepAnimationLocked(long now) { 958 if (!hasAnimations()) { 959 if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running"); 960 mFinishAnimReady = false; 961 return false; 962 } 963 964 if (!mAnimRunning) { 965 if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate"); 966 if (TWO_PHASE_ANIMATION) { 967 if (mStartEnterAnimation != null) { 968 mStartEnterAnimation.setStartTime(now); 969 } 970 if (mStartExitAnimation != null) { 971 mStartExitAnimation.setStartTime(now); 972 } 973 if (mFinishEnterAnimation != null) { 974 mFinishEnterAnimation.setStartTime(0); 975 } 976 if (mFinishExitAnimation != null) { 977 mFinishExitAnimation.setStartTime(0); 978 } 979 } 980 if (USE_CUSTOM_BLACK_FRAME) { 981 if (mStartFrameAnimation != null) { 982 mStartFrameAnimation.setStartTime(now); 983 } 984 if (mFinishFrameAnimation != null) { 985 mFinishFrameAnimation.setStartTime(0); 986 } 987 if (mRotateFrameAnimation != null) { 988 mRotateFrameAnimation.setStartTime(now); 989 } 990 } 991 if (mRotateEnterAnimation != null) { 992 mRotateEnterAnimation.setStartTime(now); 993 } 994 if (mRotateExitAnimation != null) { 995 mRotateExitAnimation.setStartTime(now); 996 } 997 mAnimRunning = true; 998 mHalfwayPoint = now + mRotateEnterAnimation.getDuration() / 2; 999 } 1000 1001 return stepAnimation(now); 1002 } 1003 getEnterTransformation()1004 public Transformation getEnterTransformation() { 1005 return mEnterTransformation; 1006 } 1007 } 1008