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