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