blob: 1a7d21406e55027bef0779c5dd582f69ee6ab7a7 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
/**
* This class handles the rotation animation when the device is rotated.
*
* <p>
* The screen rotation animation is composed of 4 different part:
* <ul>
* <li> The screenshot: <p>
* A screenshot of the whole screen prior the change of orientation is taken to hide the
* element resizing below. The screenshot is then animated to rotate and cross-fade to
* the new orientation with the content in the new orientation.
*
* <li> The windows on the display: <p>y
* Once the device is rotated, the screen and its content are in the new orientation. The
* animation first rotate the new content into the old orientation to then be able to
* animate to the new orientation
*
* <li> The exiting Blackframe: <p>
* Because the change of orientation might change the width and height of the content (i.e
* when rotating from portrait to landscape) we "crop" the new content using black frames
* around the screenshot so the new content does not go beyond the screenshot's bounds
*
* <li> The entering Blackframe: <p>
* The enter Blackframe is similar to the exit Blackframe but is only used when a custom
* rotation animation is used and matches the new content size instead of the screenshot.
* </ul>
*
* Each part has its own Surface which are then animated by {@link SurfaceAnimator}s.
*/
class ScreenRotationAnimation {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
/*
* Layers for screen rotation animation. We put these layers above
* WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
*/
private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
private final Context mContext;
private final DisplayContent mDisplayContent;
private final float[] mTmpFloats = new float[9];
private final Transformation mRotateExitTransformation = new Transformation();
private final Transformation mRotateEnterTransformation = new Transformation();
// Complete transformations being applied.
private final Transformation mExitTransformation = new Transformation();
private final Transformation mEnterTransformation = new Transformation();
private final Matrix mFrameInitialMatrix = new Matrix();
private final Matrix mSnapshotInitialMatrix = new Matrix();
private final Matrix mSnapshotFinalMatrix = new Matrix();
private final Matrix mExitFrameFinalMatrix = new Matrix();
private final WindowManagerService mService;
private SurfaceControl mEnterBlackFrameLayer;
private SurfaceControl mRotationLayer;
private SurfaceControl mSurfaceControl;
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
private int mOriginalRotation;
private int mOriginalWidth, mOriginalHeight;
private int mCurRotation;
private Rect mOriginalDisplayRect = new Rect();
private Rect mCurrentDisplayRect = new Rect();
// The current active animation to move from the old to the new rotated
// state. Which animation is run here will depend on the old and new
// rotations.
private Animation mRotateExitAnimation;
private Animation mRotateEnterAnimation;
private Animation mRotateAlphaAnimation;
private boolean mStarted;
private boolean mAnimRunning;
private boolean mFinishAnimReady;
private long mFinishAnimStartTime;
private boolean mForceDefaultOrientation;
private BlackFrame mExitingBlackFrame;
private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
mDisplayContent = displayContent;
displayContent.getBounds(mOriginalDisplayRect);
// Screenshot does NOT include rotation!
final Display display = displayContent.getDisplay();
int originalRotation = display.getRotation();
final int originalWidth;
final int originalHeight;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (fixedToUserRotation) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
originalHeight = displayContent.mBaseDisplayHeight;
} else {
// Normal situation
originalWidth = displayInfo.logicalWidth;
originalHeight = displayInfo.logicalHeight;
}
if (originalRotation == Surface.ROTATION_90
|| originalRotation == Surface.ROTATION_270) {
mWidth = originalHeight;
mHeight = originalWidth;
} else {
mWidth = originalWidth;
mHeight = originalHeight;
}
mOriginalRotation = originalRotation;
mOriginalWidth = originalWidth;
mOriginalHeight = originalHeight;
mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
mRotationLayer = displayContent.makeOverlay()
.setName("RotationLayer")
.setContainerLayer()
.build();
mEnterBlackFrameLayer = displayContent.makeOverlay()
.setName("EnterBlackFrameLayer")
.setContainerLayer()
.build();
mSurfaceControl = displayContent.makeSurface(null)
.setName("ScreenshotSurface")
.setParent(mRotationLayer)
.setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.build();
// In case display bounds change, screenshot buffer and surface may mismatch so set a
// scaling mode.
SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
t2.apply(true /* sync */);
// Capture a screenshot into the surface we just created.
final int displayId = display.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
surface.copyFrom(mSurfaceControl);
SurfaceControl.ScreenshotGraphicBuffer gb =
mService.mDisplayManagerInternal.screenshot(displayId);
if (gb != null) {
try {
surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
gb.getColorSpace());
} catch (RuntimeException e) {
Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
}
// If the screenshot contains secure layers, we have to make sure the
// screenshot surface we display it in also has FLAG_SECURE so that
// the user can not screenshot secure layers via the screenshot surface.
if (gb.containsSecureLayers()) {
t.setSecure(mSurfaceControl, true);
}
t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
t.setAlpha(mSurfaceControl, 0);
t.show(mRotationLayer);
t.show(mSurfaceControl);
} else {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
}
surface.destroy();
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" FREEZE %s: CREATE", mSurfaceControl);
setRotation(t, originalRotation);
t.apply();
}
private static void createRotationMatrix(int rotation, int width, int height,
Matrix outMatrix) {
switch (rotation) {
case Surface.ROTATION_0:
outMatrix.reset();
break;
case Surface.ROTATION_90:
outMatrix.setRotate(90, 0, 0);
outMatrix.postTranslate(height, 0);
break;
case Surface.ROTATION_180:
outMatrix.setRotate(180, 0, 0);
outMatrix.postTranslate(width, height);
break;
case Surface.ROTATION_270:
outMatrix.setRotate(270, 0, 0);
outMatrix.postTranslate(0, width);
break;
}
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(STARTED, mStarted);
proto.write(ANIMATION_RUNNING, mAnimRunning);
proto.end(token);
}
boolean hasScreenshot() {
return mSurfaceControl != null;
}
private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
if (mRotationLayer == null) {
return;
}
matrix.getValues(mTmpFloats);
float x = mTmpFloats[Matrix.MTRANS_X];
float y = mTmpFloats[Matrix.MTRANS_Y];
if (mForceDefaultOrientation) {
mDisplayContent.getBounds(mCurrentDisplayRect);
x -= mCurrentDisplayRect.left;
y -= mCurrentDisplayRect.top;
}
t.setPosition(mRotationLayer, x, y);
t.setMatrix(mRotationLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
t.setAlpha(mSurfaceControl, (float) 1.0);
t.setAlpha(mRotationLayer, (float) 1.0);
t.show(mRotationLayer);
}
public void printTo(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
pw.print(" mWidth="); pw.print(mWidth);
pw.print(" mHeight="); pw.println(mHeight);
pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
if (mExitingBlackFrame != null) {
mExitingBlackFrame.printTo(prefix + " ", pw);
}
pw.print(prefix);
pw.print("mEnteringBlackFrame=");
pw.println(mEnteringBlackFrame);
if (mEnteringBlackFrame != null) {
mEnteringBlackFrame.printTo(prefix + " ", pw);
}
pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation);
pw.print(" mOriginalRotation="); pw.println(mOriginalRotation);
pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth);
pw.print(" mOriginalHeight="); pw.println(mOriginalHeight);
pw.print(prefix); pw.print("mStarted="); pw.print(mStarted);
pw.print(" mAnimRunning="); pw.print(mAnimRunning);
pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady);
pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime);
pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation);
pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mExitTransformation=");
mExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mEnterTransformation=");
mEnterTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mFrameInitialMatrix=");
mFrameInitialMatrix.printShortString(pw);
pw.println();
pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
mSnapshotInitialMatrix.printShortString(pw);
pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
pw.println();
pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
mExitFrameFinalMatrix.printShortString(pw);
pw.println();
pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
if (mForceDefaultOrientation) {
pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
}
}
public void setRotation(SurfaceControl.Transaction t, int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
setRotationTransform(t, mSnapshotInitialMatrix);
}
/**
* Returns true if animating.
*/
private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
// Can't do animation.
return false;
}
if (mStarted) {
return true;
}
mStarted = true;
// Figure out how the screen has moved from the original rotation.
int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_alpha);
final boolean customAnim;
if (exitAnim != 0 && enterAnim != 0) {
customAnim = true;
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
} else {
customAnim = false;
switch (delta) {
case Surface.ROTATION_0:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
}
// Initialize the animations. This is a hack, redefining what "parent"
// means to allow supplying the last and next size. In this definition
// "%p" is the original (let's call it "previous") size, and "%" is the
// screen's current/new size.
mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mAnimRunning = false;
mFinishAnimReady = false;
mFinishAnimStartTime = -1;
mRotateExitAnimation.restrictDuration(maxAnimationDuration);
mRotateExitAnimation.scaleCurrentDuration(animationScale);
mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
if (!customAnim && mExitingBlackFrame == null) {
try {
// Compute the transformation matrix that must be applied
// the the black frame to make it stay in the initial position
// before the new screen rotation. This is different than the
// snapshot transformation because the snapshot is always based
// of the native orientation of the screen, not the orientation
// we were last in.
createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
final Rect outer;
final Rect inner;
if (mForceDefaultOrientation) {
// Going from a smaller Display to a larger Display, add curtains to sides
// or top and bottom. Going from a larger to smaller display will result in
// no BlackSurfaces being constructed.
outer = mCurrentDisplayRect;
inner = mOriginalDisplayRect;
} else {
outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
inner = new Rect(0, 0, mWidth, mHeight);
}
mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
mRotationLayer);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
}
}
if (customAnim && mEnteringBlackFrame == null) {
try {
Rect outer = new Rect(-finalWidth, -finalHeight,
finalWidth * 2, finalHeight * 2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
}
}
mSurfaceRotationAnimationController.startAnimation();
return true;
}
/**
* Returns true if animating.
*/
public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
if (mSurfaceControl == null) {
// Can't do animation.
return false;
}
if (!mStarted) {
startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
exitAnim, enterAnim);
}
if (!mStarted) {
return false;
}
mFinishAnimReady = true;
return true;
}
public void kill() {
if (mSurfaceRotationAnimationController != null) {
mSurfaceRotationAnimationController.cancel();
mSurfaceRotationAnimationController = null;
}
if (mSurfaceControl != null) {
ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mSurfaceControl);
mSurfaceControl = null;
SurfaceControl.Transaction t = mService.mTransactionFactory.get();
if (mRotationLayer != null) {
if (mRotationLayer.isValid()) {
t.remove(mRotationLayer);
}
mRotationLayer = null;
}
if (mEnterBlackFrameLayer != null) {
if (mEnterBlackFrameLayer.isValid()) {
t.remove(mEnterBlackFrameLayer);
}
mEnterBlackFrameLayer = null;
}
t.apply();
}
if (mExitingBlackFrame != null) {
mExitingBlackFrame.kill();
mExitingBlackFrame = null;
}
if (mEnteringBlackFrame != null) {
mEnteringBlackFrame.kill();
mEnteringBlackFrame = null;
}
if (mRotateExitAnimation != null) {
mRotateExitAnimation.cancel();
mRotateExitAnimation = null;
}
if (mRotateEnterAnimation != null) {
mRotateEnterAnimation.cancel();
mRotateEnterAnimation = null;
}
if (mRotateAlphaAnimation != null) {
mRotateAlphaAnimation.cancel();
mRotateAlphaAnimation = null;
}
}
public boolean isAnimating() {
return mSurfaceRotationAnimationController != null
&& mSurfaceRotationAnimationController.isAnimating();
}
public boolean isRotating() {
return mCurRotation != mOriginalRotation;
}
public Transformation getEnterTransformation() {
return mEnterTransformation;
}
/**
* Utility class that runs a {@link ScreenRotationAnimation} on the {@link
* SurfaceAnimationRunner}.
* <p>
* The rotation animation is divided into the following hierarchy:
* <ul>
* <li> A first rotation layer, containing the blackframes. This layer is animated by the
* "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
* <ul>
* <li> A child layer containing the screenshot on which is added an animation of it's
* alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
* </ul>
* <li> A second rotation layer used when custom animations are passed in
* {@link ScreenRotationAnimation#startAnimation(
* SurfaceControl.Transaction, long, float, int, int, int, int)}.
* </ul>
* <p>
* Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
* this three {@link SurfaceControl}s which then delegates the animation to the
* {@link ScreenRotationAnimation}.
*/
class SurfaceRotationAnimationController {
private SurfaceAnimator mDisplayAnimator;
private SurfaceAnimator mEnterBlackFrameAnimator;
private SurfaceAnimator mScreenshotRotationAnimator;
private SurfaceAnimator mRotateScreenAnimator;
/**
* Start the rotation animation of the display and the screenshot on the
* {@link SurfaceAnimationRunner}.
*/
void startAnimation() {
mRotateScreenAnimator = startScreenshotAlphaAnimation();
mDisplayAnimator = startDisplayRotation();
if (mExitingBlackFrame != null) {
mScreenshotRotationAnimator = startScreenshotRotationAnimation();
}
if (mEnteringBlackFrame != null) {
mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
}
}
private SimpleSurfaceAnimatable.Builder initializeBuilder() {
return new SimpleSurfaceAnimatable.Builder()
.setPendingTransactionSupplier(mDisplayContent::getPendingTransaction)
.setCommitTransactionRunnable(mDisplayContent::commitPendingTransaction)
.setAnimationLeashSupplier(mDisplayContent::makeOverlay);
}
private SurfaceAnimator startDisplayRotation() {
return startAnimation(initializeBuilder()
.setAnimationLeashParent(mDisplayContent.getSurfaceControl())
.setSurfaceControl(mDisplayContent.getWindowingLayer())
.setParentSurfaceControl(mDisplayContent.getSurfaceControl())
.setWidth(mDisplayContent.getSurfaceWidth())
.setHeight(mDisplayContent.getSurfaceHeight())
.build(),
createWindowAnimationSpec(mRotateEnterAnimation),
this::onAnimationEnd);
}
private SurfaceAnimator startScreenshotAlphaAnimation() {
return startAnimation(initializeBuilder()
.setSurfaceControl(mSurfaceControl)
.setAnimationLeashParent(mRotationLayer)
.setWidth(mWidth)
.setHeight(mHeight)
.build(),
createWindowAnimationSpec(mRotateAlphaAnimation),
this::onAnimationEnd);
}
private SurfaceAnimator startEnterBlackFrameAnimation() {
return startAnimation(initializeBuilder()
.setSurfaceControl(mEnterBlackFrameLayer)
.setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.build(),
createWindowAnimationSpec(mRotateEnterAnimation),
this::onAnimationEnd);
}
private SurfaceAnimator startScreenshotRotationAnimation() {
return startAnimation(initializeBuilder()
.setSurfaceControl(mRotationLayer)
.setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.build(),
createWindowAnimationSpec(mRotateExitAnimation),
this::onAnimationEnd);
}
private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
}
/**
* Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
*
* @param animatable The animatable used for the animation.
* @param animationSpec The spec of the animation.
* @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator} and
* called when the animation finishes.
* @return The newly created {@link SurfaceAnimator} that as been started.
*/
private SurfaceAnimator startAnimation(
SurfaceAnimator.Animatable animatable,
LocalAnimationAdapter.AnimationSpec animationSpec,
Runnable animationFinishedCallback) {
SurfaceAnimator animator = new SurfaceAnimator(
animatable, animationFinishedCallback, mService);
LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
animationSpec, mService.mSurfaceAnimationRunner);
animator.startAnimation(mDisplayContent.getPendingTransaction(),
localAnimationAdapter, false);
return animator;
}
private void onAnimationEnd() {
synchronized (mService.mGlobalLock) {
if (isAnimating()) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"ScreenRotation sill animating: mDisplayAnimator: %s\n"
+ "mEnterBlackFrameAnimator: "
+ "%s\nmRotateScreenAnimator: %s\n"
+ "mScreenshotRotationAnimator: %s",
mDisplayAnimator != null
? mDisplayAnimator.isAnimating() : null,
mEnterBlackFrameAnimator != null
? mEnterBlackFrameAnimator.isAnimating() : null,
mRotateScreenAnimator != null
? mRotateScreenAnimator.isAnimating() : null,
mScreenshotRotationAnimator != null
? mScreenshotRotationAnimator.isAnimating() : null
);
return;
}
ProtoLog.d(WM_DEBUG_ORIENTATION, "ScreenRotationAnimation onAnimationEnd");
mEnterBlackFrameAnimator = null;
mScreenshotRotationAnimator = null;
mRotateScreenAnimator = null;
mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
kill();
mService.updateRotation(false, false);
AccessibilityController accessibilityController = mService.mAccessibilityController;
if (accessibilityController != null) {
// We just finished rotation animation which means we did not
// announce the rotation and waited for it to end, announce now.
accessibilityController.onRotationChangedLocked(mDisplayContent);
}
}
}
public void cancel() {
if (mEnterBlackFrameAnimator != null) {
mEnterBlackFrameAnimator.cancelAnimation();
}
if (mScreenshotRotationAnimator != null) {
mScreenshotRotationAnimator.cancelAnimation();
}
if (mRotateScreenAnimator != null) {
mRotateScreenAnimator.cancelAnimation();
}
if (mDisplayAnimator != null) {
mDisplayAnimator.cancelAnimation();
}
}
public boolean isAnimating() {
return mDisplayAnimator != null && mDisplayAnimator.isAnimating()
|| mEnterBlackFrameAnimator != null && mEnterBlackFrameAnimator.isAnimating()
|| mRotateScreenAnimator != null && mRotateScreenAnimator.isAnimating()
|| mScreenshotRotationAnimator != null
&& mScreenshotRotationAnimator.isAnimating();
}
}
}