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 android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 21 import static com.android.server.wm.AnimationSpecProto.ROTATE; 22 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; 23 import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; 24 import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS; 25 import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA; 26 import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA; 27 import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING; 28 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED; 29 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; 30 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 31 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 32 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; 33 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER; 34 35 import android.animation.ArgbEvaluator; 36 import android.content.Context; 37 import android.graphics.Color; 38 import android.graphics.Matrix; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.os.Trace; 42 import android.util.Slog; 43 import android.util.proto.ProtoOutputStream; 44 import android.view.DisplayInfo; 45 import android.view.Surface; 46 import android.view.Surface.OutOfResourcesException; 47 import android.view.SurfaceControl; 48 import android.view.animation.Animation; 49 import android.view.animation.AnimationUtils; 50 import android.view.animation.Transformation; 51 52 import com.android.internal.R; 53 import com.android.server.protolog.common.ProtoLog; 54 import com.android.server.wm.SurfaceAnimator.AnimationType; 55 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 56 import com.android.server.wm.utils.RotationAnimationUtils; 57 58 import java.io.PrintWriter; 59 60 /** 61 * This class handles the rotation animation when the device is rotated. 62 * 63 * <p> 64 * The screen rotation animation is composed of 4 different part: 65 * <ul> 66 * <li> The screenshot: <p> 67 * A screenshot of the whole screen prior the change of orientation is taken to hide the 68 * element resizing below. The screenshot is then animated to rotate and cross-fade to 69 * the new orientation with the content in the new orientation. 70 * 71 * <li> The windows on the display: <p>y 72 * Once the device is rotated, the screen and its content are in the new orientation. The 73 * animation first rotate the new content into the old orientation to then be able to 74 * animate to the new orientation 75 * 76 * <li> The Background color frame: <p> 77 * To have the animation seem more seamless, we add a color transitioning background behind the 78 * exiting and entering layouts. We compute the brightness of the start and end 79 * layouts and transition from the two brightness values as grayscale underneath the animation 80 * 81 * <li> The entering Blackframe: <p> 82 * The enter Blackframe is similar to the exit Blackframe but is only used when a custom 83 * rotation animation is used and matches the new content size instead of the screenshot. 84 * </ul> 85 * 86 * Each part has its own Surface which are then animated by {@link SurfaceAnimator}s. 87 */ 88 class ScreenRotationAnimation { 89 private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM; 90 91 /* 92 * Layers for screen rotation animation. We put these layers above 93 * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows. 94 */ 95 private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER; 96 private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE; 97 98 private final Context mContext; 99 private final DisplayContent mDisplayContent; 100 private final float[] mTmpFloats = new float[9]; 101 private final Transformation mRotateExitTransformation = new Transformation(); 102 private final Transformation mRotateEnterTransformation = new Transformation(); 103 // Complete transformations being applied. 104 private final Transformation mEnterTransformation = new Transformation(); 105 private final Matrix mSnapshotInitialMatrix = new Matrix(); 106 private final WindowManagerService mService; 107 /** Only used for custom animations and not screen rotation. */ 108 private SurfaceControl mEnterBlackFrameLayer; 109 /** This layer contains the actual screenshot that is to be faded out. */ 110 private SurfaceControl mScreenshotLayer; 111 /** 112 * Only used for screen rotation and not custom animations. Layered behind all other layers 113 * to avoid showing any "empty" spots 114 */ 115 private SurfaceControl mBackColorSurface; 116 private BlackFrame mEnteringBlackFrame; 117 private int mWidth, mHeight; 118 119 private final int mOriginalRotation; 120 private final int mOriginalWidth; 121 private final int mOriginalHeight; 122 private int mCurRotation; 123 124 private Rect mOriginalDisplayRect = new Rect(); 125 private Rect mCurrentDisplayRect = new Rect(); 126 // The current active animation to move from the old to the new rotated 127 // state. Which animation is run here will depend on the old and new 128 // rotations. 129 private Animation mRotateExitAnimation; 130 private Animation mRotateEnterAnimation; 131 private Animation mRotateAlphaAnimation; 132 private boolean mStarted; 133 private boolean mAnimRunning; 134 private boolean mFinishAnimReady; 135 private long mFinishAnimStartTime; 136 private boolean mForceDefaultOrientation; 137 private SurfaceRotationAnimationController mSurfaceRotationAnimationController; 138 /** Intensity of light/whiteness of the layout before rotation occurs. */ 139 private float mStartLuma; 140 /** Intensity of light/whiteness of the layout after rotation occurs. */ 141 private float mEndLuma; 142 ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation)143 ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) { 144 mService = displayContent.mWmService; 145 mContext = mService.mContext; 146 mDisplayContent = displayContent; 147 displayContent.getBounds(mOriginalDisplayRect); 148 149 // Screenshot does NOT include rotation! 150 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); 151 final int realOriginalRotation = displayInfo.rotation; 152 final int originalWidth; 153 final int originalHeight; 154 if (displayContent.getDisplayRotation().isFixedToUserRotation()) { 155 // Emulated orientation. 156 mForceDefaultOrientation = true; 157 originalWidth = displayContent.mBaseDisplayWidth; 158 originalHeight = displayContent.mBaseDisplayHeight; 159 } else { 160 // Normal situation 161 originalWidth = displayInfo.logicalWidth; 162 originalHeight = displayInfo.logicalHeight; 163 } 164 if (realOriginalRotation == Surface.ROTATION_90 165 || realOriginalRotation == Surface.ROTATION_270) { 166 mWidth = originalHeight; 167 mHeight = originalWidth; 168 } else { 169 mWidth = originalWidth; 170 mHeight = originalHeight; 171 } 172 173 mOriginalRotation = originalRotation; 174 // If the delta is not zero, the rotation of display may not change, but we still want to 175 // apply rotation animation because there should be a top app shown as rotated. So the 176 // specified original rotation customizes the direction of animation to have better look 177 // when restoring the rotated app to the same rotation as current display. 178 final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation); 179 final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270; 180 mOriginalWidth = flipped ? originalHeight : originalWidth; 181 mOriginalHeight = flipped ? originalWidth : originalHeight; 182 mSurfaceRotationAnimationController = new SurfaceRotationAnimationController(); 183 184 // Check whether the current screen contains any secure content. 185 final boolean isSecure = displayContent.hasSecureWindowOnScreen(); 186 final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); 187 try { 188 mBackColorSurface = displayContent.makeChildSurface(null) 189 .setName("BackColorSurface") 190 .setColorLayer() 191 .setCallsite("ScreenRotationAnimation") 192 .build(); 193 194 mScreenshotLayer = displayContent.makeOverlay() 195 .setName("RotationLayer") 196 .setBufferSize(mWidth, mHeight) 197 .setSecure(isSecure) 198 .setCallsite("ScreenRotationAnimation") 199 .build(); 200 201 mEnterBlackFrameLayer = displayContent.makeOverlay() 202 .setName("EnterBlackFrameLayer") 203 .setContainerLayer() 204 .setCallsite("ScreenRotationAnimation") 205 .build(); 206 207 // In case display bounds change, screenshot buffer and surface may mismatch so set a 208 // scaling mode. 209 SurfaceControl.Transaction t2 = mService.mTransactionFactory.get(); 210 t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW); 211 t2.apply(true /* sync */); 212 213 // Capture a screenshot into the surface we just created. 214 final int displayId = displayContent.getDisplayId(); 215 final Surface surface = mService.mSurfaceFactory.get(); 216 surface.copyFrom(mScreenshotLayer); 217 SurfaceControl.ScreenshotGraphicBuffer gb = 218 mService.mDisplayManagerInternal.systemScreenshot(displayId); 219 if (gb != null) { 220 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, 221 "ScreenRotationAnimation#getMedianBorderLuma"); 222 mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(), 223 gb.getColorSpace()); 224 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 225 try { 226 surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(), 227 gb.getColorSpace()); 228 } catch (RuntimeException e) { 229 Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage()); 230 } 231 // If the screenshot contains secure layers, we have to make sure the 232 // screenshot surface we display it in also has FLAG_SECURE so that 233 // the user can not screenshot secure layers via the screenshot surface. 234 if (gb.containsSecureLayers()) { 235 t.setSecure(mScreenshotLayer, true); 236 } 237 t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); 238 t.reparent(mBackColorSurface, displayContent.getSurfaceControl()); 239 t.setLayer(mBackColorSurface, -1); 240 t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma}); 241 t.setAlpha(mBackColorSurface, 1); 242 t.show(mScreenshotLayer); 243 t.show(mBackColorSurface); 244 } else { 245 Slog.w(TAG, "Unable to take screenshot of display " + displayId); 246 } 247 surface.destroy(); 248 } catch (OutOfResourcesException e) { 249 Slog.w(TAG, "Unable to allocate freeze surface", e); 250 } 251 252 ProtoLog.i(WM_SHOW_SURFACE_ALLOC, 253 " FREEZE %s: CREATE", mScreenshotLayer); 254 setRotation(t, realOriginalRotation); 255 t.apply(); 256 } 257 dumpDebug(ProtoOutputStream proto, long fieldId)258 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 259 final long token = proto.start(fieldId); 260 proto.write(STARTED, mStarted); 261 proto.write(ANIMATION_RUNNING, mAnimRunning); 262 proto.end(token); 263 } 264 hasScreenshot()265 boolean hasScreenshot() { 266 return mScreenshotLayer != null; 267 } 268 setRotationTransform(SurfaceControl.Transaction t, Matrix matrix)269 private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) { 270 if (mScreenshotLayer == null) { 271 return; 272 } 273 matrix.getValues(mTmpFloats); 274 float x = mTmpFloats[Matrix.MTRANS_X]; 275 float y = mTmpFloats[Matrix.MTRANS_Y]; 276 if (mForceDefaultOrientation) { 277 mDisplayContent.getBounds(mCurrentDisplayRect); 278 x -= mCurrentDisplayRect.left; 279 y -= mCurrentDisplayRect.top; 280 } 281 t.setPosition(mScreenshotLayer, x, y); 282 t.setMatrix(mScreenshotLayer, 283 mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y], 284 mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]); 285 286 t.setAlpha(mScreenshotLayer, (float) 1.0); 287 t.show(mScreenshotLayer); 288 } 289 printTo(String prefix, PrintWriter pw)290 public void printTo(String prefix, PrintWriter pw) { 291 pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer); 292 pw.print(" mWidth="); pw.print(mWidth); 293 pw.print(" mHeight="); pw.println(mHeight); 294 pw.print(prefix); 295 pw.print("mEnteringBlackFrame="); 296 pw.println(mEnteringBlackFrame); 297 if (mEnteringBlackFrame != null) { 298 mEnteringBlackFrame.printTo(prefix + " ", pw); 299 } 300 pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation); 301 pw.print(" mOriginalRotation="); pw.println(mOriginalRotation); 302 pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth); 303 pw.print(" mOriginalHeight="); pw.println(mOriginalHeight); 304 pw.print(prefix); pw.print("mStarted="); pw.print(mStarted); 305 pw.print(" mAnimRunning="); pw.print(mAnimRunning); 306 pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady); 307 pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime); 308 pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation); 309 pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println(); 310 pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation); 311 pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println(); 312 pw.print(prefix); pw.print("mEnterTransformation="); 313 mEnterTransformation.printShortString(pw); pw.println(); 314 pw.print(prefix); pw.print("mSnapshotInitialMatrix="); 315 mSnapshotInitialMatrix.printShortString(pw);pw.println(); 316 pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation); 317 if (mForceDefaultOrientation) { 318 pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString()); 319 pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString()); 320 } 321 } 322 setRotation(SurfaceControl.Transaction t, int rotation)323 public void setRotation(SurfaceControl.Transaction t, int rotation) { 324 mCurRotation = rotation; 325 326 // Compute the transformation matrix that must be applied 327 // to the snapshot to make it stay in the same original position 328 // with the current screen rotation. 329 int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0); 330 RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); 331 332 setRotationTransform(t, mSnapshotInitialMatrix); 333 } 334 335 /** 336 * Returns true if animating. 337 */ startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)338 private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, 339 float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { 340 if (mScreenshotLayer == null) { 341 // Can't do animation. 342 return false; 343 } 344 if (mStarted) { 345 return true; 346 } 347 348 mStarted = true; 349 350 // Figure out how the screen has moved from the original rotation. 351 int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation); 352 353 354 final boolean customAnim; 355 if (exitAnim != 0 && enterAnim != 0) { 356 customAnim = true; 357 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim); 358 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim); 359 mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext, 360 R.anim.screen_rotate_alpha); 361 } else { 362 customAnim = false; 363 switch (delta) { /* Counter-Clockwise Rotations */ 364 case Surface.ROTATION_0: 365 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 366 R.anim.screen_rotate_0_exit); 367 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 368 R.anim.screen_rotate_0_enter); 369 break; 370 case Surface.ROTATION_90: 371 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 372 R.anim.screen_rotate_plus_90_exit); 373 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 374 R.anim.screen_rotate_plus_90_enter); 375 break; 376 case Surface.ROTATION_180: 377 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 378 R.anim.screen_rotate_180_exit); 379 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 380 R.anim.screen_rotate_180_enter); 381 break; 382 case Surface.ROTATION_270: 383 mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, 384 R.anim.screen_rotate_minus_90_exit); 385 mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, 386 R.anim.screen_rotate_minus_90_enter); 387 break; 388 } 389 } 390 391 ProtoLog.d(WM_DEBUG_ORIENTATION, "Start rotation animation. customAnim=%s, " 392 + "mCurRotation=%s, mOriginalRotation=%s", 393 customAnim, Surface.rotationToString(mCurRotation), 394 Surface.rotationToString(mOriginalRotation)); 395 396 mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 397 mRotateExitAnimation.restrictDuration(maxAnimationDuration); 398 mRotateExitAnimation.scaleCurrentDuration(animationScale); 399 mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight); 400 mRotateEnterAnimation.restrictDuration(maxAnimationDuration); 401 mRotateEnterAnimation.scaleCurrentDuration(animationScale); 402 403 mAnimRunning = false; 404 mFinishAnimReady = false; 405 mFinishAnimStartTime = -1; 406 407 if (customAnim) { 408 mRotateAlphaAnimation.restrictDuration(maxAnimationDuration); 409 mRotateAlphaAnimation.scaleCurrentDuration(animationScale); 410 } 411 412 if (customAnim && mEnteringBlackFrame == null) { 413 try { 414 Rect outer = new Rect(-finalWidth, -finalHeight, 415 finalWidth * 2, finalHeight * 2); 416 Rect inner = new Rect(0, 0, finalWidth, finalHeight); 417 mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner, 418 SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer); 419 } catch (OutOfResourcesException e) { 420 Slog.w(TAG, "Unable to allocate black surface", e); 421 } 422 } 423 424 if (customAnim) { 425 mSurfaceRotationAnimationController.startCustomAnimation(); 426 } else { 427 mSurfaceRotationAnimationController.startScreenRotationAnimation(); 428 } 429 430 return true; 431 } 432 433 /** 434 * Returns true if animating. 435 */ dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)436 public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, 437 float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) { 438 if (mScreenshotLayer == null) { 439 // Can't do animation. 440 return false; 441 } 442 if (!mStarted) { 443 mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(), 444 mDisplayContent.getWindowingLayer()); 445 startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, 446 exitAnim, enterAnim); 447 } 448 if (!mStarted) { 449 return false; 450 } 451 mFinishAnimReady = true; 452 return true; 453 } 454 kill()455 public void kill() { 456 if (mSurfaceRotationAnimationController != null) { 457 mSurfaceRotationAnimationController.cancel(); 458 mSurfaceRotationAnimationController = null; 459 } 460 461 if (mScreenshotLayer != null) { 462 ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mScreenshotLayer); 463 SurfaceControl.Transaction t = mService.mTransactionFactory.get(); 464 if (mScreenshotLayer.isValid()) { 465 t.remove(mScreenshotLayer); 466 } 467 mScreenshotLayer = null; 468 469 if (mEnterBlackFrameLayer != null) { 470 if (mEnterBlackFrameLayer.isValid()) { 471 t.remove(mEnterBlackFrameLayer); 472 } 473 mEnterBlackFrameLayer = null; 474 } 475 if (mBackColorSurface != null) { 476 if (mBackColorSurface.isValid()) { 477 t.remove(mBackColorSurface); 478 } 479 mBackColorSurface = null; 480 } 481 t.apply(); 482 } 483 484 if (mEnteringBlackFrame != null) { 485 mEnteringBlackFrame.kill(); 486 mEnteringBlackFrame = null; 487 } 488 if (mRotateExitAnimation != null) { 489 mRotateExitAnimation.cancel(); 490 mRotateExitAnimation = null; 491 } 492 if (mRotateEnterAnimation != null) { 493 mRotateEnterAnimation.cancel(); 494 mRotateEnterAnimation = null; 495 } 496 if (mRotateAlphaAnimation != null) { 497 mRotateAlphaAnimation.cancel(); 498 mRotateAlphaAnimation = null; 499 } 500 } 501 isAnimating()502 public boolean isAnimating() { 503 return mSurfaceRotationAnimationController != null 504 && mSurfaceRotationAnimationController.isAnimating(); 505 } 506 isRotating()507 public boolean isRotating() { 508 return mCurRotation != mOriginalRotation; 509 } 510 getEnterTransformation()511 public Transformation getEnterTransformation() { 512 return mEnterTransformation; 513 } 514 515 /** 516 * Utility class that runs a {@link ScreenRotationAnimation} on the {@link 517 * SurfaceAnimationRunner}. 518 * <p> 519 * The rotation animation supports both screen rotation and custom animations 520 * 521 * For custom animations: 522 * <ul> 523 * <li> 524 * The screenshot layer which has an added animation of it's alpha channel 525 * ("screen_rotate_alpha") and that will be applied along with the custom animation. 526 * </li> 527 * <li> A device layer that is animated with the provided custom animation </li> 528 * </ul> 529 * 530 * For screen rotation: 531 * <ul> 532 * <li> A rotation layer that is both rotated and faded out during a single animation </li> 533 * <li> A device layer that is both rotated and faded in during a single animation </li> 534 * <li> A background color layer that transitions colors behind the first two layers </li> 535 * </ul> 536 * 537 * {@link ScreenRotationAnimation#startAnimation( 538 * SurfaceControl.Transaction, long, float, int, int, int, int)}. 539 * </ul> 540 * 541 * <p> 542 * Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of 543 * this three {@link SurfaceControl}s which then delegates the animation to the 544 * {@link ScreenRotationAnimation}. 545 */ 546 class SurfaceRotationAnimationController { 547 private SurfaceAnimator mDisplayAnimator; 548 private SurfaceAnimator mScreenshotRotationAnimator; 549 private SurfaceAnimator mRotateScreenAnimator; 550 private SurfaceAnimator mEnterBlackFrameAnimator; 551 startCustomAnimation()552 void startCustomAnimation() { 553 try { 554 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 555 mRotateScreenAnimator = startScreenshotAlphaAnimation(); 556 mDisplayAnimator = startDisplayRotation(); 557 if (mEnteringBlackFrame != null) { 558 mEnterBlackFrameAnimator = startEnterBlackFrameAnimation(); 559 } 560 } finally { 561 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 562 } 563 } 564 565 /** 566 * Start the rotation animation of the display and the screenshot on the 567 * {@link SurfaceAnimationRunner}. 568 */ startScreenRotationAnimation()569 void startScreenRotationAnimation() { 570 try { 571 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 572 mDisplayAnimator = startDisplayRotation(); 573 mScreenshotRotationAnimator = startScreenshotRotationAnimation(); 574 startColorAnimation(); 575 } finally { 576 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 577 } 578 } 579 initializeBuilder()580 private SimpleSurfaceAnimatable.Builder initializeBuilder() { 581 return new SimpleSurfaceAnimatable.Builder() 582 .setPendingTransactionSupplier(mDisplayContent::getPendingTransaction) 583 .setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction) 584 .setAnimationLeashSupplier(mDisplayContent::makeOverlay); 585 } 586 startDisplayRotation()587 private SurfaceAnimator startDisplayRotation() { 588 return startAnimation(initializeBuilder() 589 .setAnimationLeashParent(mDisplayContent.getSurfaceControl()) 590 .setSurfaceControl(mDisplayContent.getWindowingLayer()) 591 .setParentSurfaceControl(mDisplayContent.getSurfaceControl()) 592 .setWidth(mDisplayContent.getSurfaceWidth()) 593 .setHeight(mDisplayContent.getSurfaceHeight()) 594 .build(), 595 createWindowAnimationSpec(mRotateEnterAnimation), 596 this::onAnimationEnd); 597 } 598 startScreenshotAlphaAnimation()599 private SurfaceAnimator startScreenshotAlphaAnimation() { 600 return startAnimation(initializeBuilder() 601 .setSurfaceControl(mScreenshotLayer) 602 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 603 .setWidth(mDisplayContent.getSurfaceWidth()) 604 .setHeight(mDisplayContent.getSurfaceHeight()) 605 .build(), 606 createWindowAnimationSpec(mRotateAlphaAnimation), 607 this::onAnimationEnd); 608 } 609 startEnterBlackFrameAnimation()610 private SurfaceAnimator startEnterBlackFrameAnimation() { 611 return startAnimation(initializeBuilder() 612 .setSurfaceControl(mEnterBlackFrameLayer) 613 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 614 .build(), 615 createWindowAnimationSpec(mRotateEnterAnimation), 616 this::onAnimationEnd); 617 } 618 startScreenshotRotationAnimation()619 private SurfaceAnimator startScreenshotRotationAnimation() { 620 return startAnimation(initializeBuilder() 621 .setSurfaceControl(mScreenshotLayer) 622 .setAnimationLeashParent(mDisplayContent.getOverlayLayer()) 623 .build(), 624 createWindowAnimationSpec(mRotateExitAnimation), 625 this::onAnimationEnd); 626 } 627 628 629 /** 630 * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a 631 * grayscale color 632 */ startColorAnimation()633 private void startColorAnimation() { 634 int colorTransitionMs = mContext.getResources().getInteger( 635 R.integer.config_screen_rotation_color_transition); 636 final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner; 637 final float[] rgbTmpFloat = new float[3]; 638 final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma); 639 final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma); 640 final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale(); 641 final ArgbEvaluator va = ArgbEvaluator.getInstance(); 642 runner.startAnimation( 643 new LocalAnimationAdapter.AnimationSpec() { 644 @Override 645 public long getDuration() { 646 return duration; 647 } 648 649 @Override 650 public void apply(SurfaceControl.Transaction t, SurfaceControl leash, 651 long currentPlayTime) { 652 final float fraction = getFraction(currentPlayTime); 653 final int color = (Integer) va.evaluate(fraction, startColor, endColor); 654 Color middleColor = Color.valueOf(color); 655 rgbTmpFloat[0] = middleColor.red(); 656 rgbTmpFloat[1] = middleColor.green(); 657 rgbTmpFloat[2] = middleColor.blue(); 658 if (leash.isValid()) { 659 t.setColor(leash, rgbTmpFloat); 660 } 661 } 662 663 @Override 664 public void dump(PrintWriter pw, String prefix) { 665 pw.println(prefix + "startLuma=" + mStartLuma 666 + " endLuma=" + mEndLuma 667 + " durationMs=" + colorTransitionMs); 668 } 669 670 @Override 671 public void dumpDebugInner(ProtoOutputStream proto) { 672 final long token = proto.start(ROTATE); 673 proto.write(START_LUMA, mStartLuma); 674 proto.write(END_LUMA, mEndLuma); 675 proto.write(DURATION_MS, colorTransitionMs); 676 proto.end(token); 677 } 678 }, 679 mBackColorSurface, mDisplayContent.getPendingTransaction(), null); 680 } 681 createWindowAnimationSpec(Animation mAnimation)682 private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) { 683 return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */, 684 false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */); 685 } 686 687 /** 688 * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}. 689 * 690 * @param animatable The animatable used for the animation. 691 * @param animationSpec The spec of the animation. 692 * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator} 693 * and called when the animation finishes. 694 * @return The newly created {@link SurfaceAnimator} that as been started. 695 */ startAnimation( SurfaceAnimator.Animatable animatable, LocalAnimationAdapter.AnimationSpec animationSpec, OnAnimationFinishedCallback animationFinishedCallback)696 private SurfaceAnimator startAnimation( 697 SurfaceAnimator.Animatable animatable, 698 LocalAnimationAdapter.AnimationSpec animationSpec, 699 OnAnimationFinishedCallback animationFinishedCallback) { 700 SurfaceAnimator animator = new SurfaceAnimator( 701 animatable, animationFinishedCallback, mService); 702 703 LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter( 704 animationSpec, mService.mSurfaceAnimationRunner); 705 animator.startAnimation(mDisplayContent.getPendingTransaction(), 706 localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION); 707 return animator; 708 } 709 onAnimationEnd(@nimationType int type, AnimationAdapter anim)710 private void onAnimationEnd(@AnimationType int type, AnimationAdapter anim) { 711 synchronized (mService.mGlobalLock) { 712 if (isAnimating()) { 713 ProtoLog.v(WM_DEBUG_ORIENTATION, 714 "ScreenRotation still animating: type: %d\n" 715 + "mDisplayAnimator: %s\n" 716 + "mEnterBlackFrameAnimator: %s\n" 717 + "mRotateScreenAnimator: %s\n" 718 + "mScreenshotRotationAnimator: %s", 719 type, 720 mDisplayAnimator != null 721 ? mDisplayAnimator.isAnimating() : null, 722 mEnterBlackFrameAnimator != null 723 ? mEnterBlackFrameAnimator.isAnimating() : null, 724 mRotateScreenAnimator != null 725 ? mRotateScreenAnimator.isAnimating() : null, 726 mScreenshotRotationAnimator != null 727 ? mScreenshotRotationAnimator.isAnimating() : null 728 ); 729 return; 730 } 731 ProtoLog.d(WM_DEBUG_ORIENTATION, "ScreenRotationAnimation onAnimationEnd"); 732 mEnterBlackFrameAnimator = null; 733 mScreenshotRotationAnimator = null; 734 mRotateScreenAnimator = null; 735 mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION; 736 kill(); 737 mService.updateRotation(false, false); 738 } 739 } 740 cancel()741 public void cancel() { 742 if (mEnterBlackFrameAnimator != null) { 743 mEnterBlackFrameAnimator.cancelAnimation(); 744 } 745 if (mScreenshotRotationAnimator != null) { 746 mScreenshotRotationAnimator.cancelAnimation(); 747 } 748 749 if (mRotateScreenAnimator != null) { 750 mRotateScreenAnimator.cancelAnimation(); 751 } 752 753 if (mDisplayAnimator != null) { 754 mDisplayAnimator.cancelAnimation(); 755 } 756 757 if (mBackColorSurface != null) { 758 mService.mSurfaceAnimationRunner.onAnimationCancelled(mBackColorSurface); 759 } 760 } 761 isAnimating()762 public boolean isAnimating() { 763 return mDisplayAnimator != null && mDisplayAnimator.isAnimating() 764 || mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating() 765 || mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating() 766 || mScreenshotRotationAnimator != null 767 && mScreenshotRotationAnimator.isAnimating(); 768 } 769 } 770 } 771