Merge "Apply rotation animation to restore rotated activity" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-04-14 01:27:54 +00:00
committed by Android (Google) Code Review
7 changed files with 170 additions and 46 deletions

View File

@@ -5125,26 +5125,48 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void startFreezingScreen() {
startFreezingScreen(ROTATION_UNDEFINED /* overrideOriginalDisplayRotation */);
}
void startFreezingScreen(int overrideOriginalDisplayRotation) {
ProtoLog.i(WM_DEBUG_ORIENTATION,
"Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
appToken, isVisible(), mFreezingScreen, mVisibleRequested,
new RuntimeException().fillInStackTrace());
if (mVisibleRequested) {
if (!mFreezingScreen) {
mFreezingScreen = true;
mWmService.registerAppFreezeListener(this);
mWmService.mAppsFreezingScreen++;
if (mWmService.mAppsFreezingScreen == 1) {
mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
if (!mVisibleRequested) {
return;
}
// If the override is given, the rotation of display doesn't change but we still want to
// cover the activity whose configuration is changing by freezing the display and running
// the rotation animation.
final boolean forceRotation = overrideOriginalDisplayRotation != ROTATION_UNDEFINED;
if (!mFreezingScreen) {
mFreezingScreen = true;
mWmService.registerAppFreezeListener(this);
mWmService.mAppsFreezingScreen++;
if (mWmService.mAppsFreezingScreen == 1) {
if (forceRotation) {
// Make sure normal rotation animation will be applied.
mDisplayContent.getDisplayRotation().cancelSeamlessRotation();
}
mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */,
mDisplayContent, overrideOriginalDisplayRotation);
mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
}
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
final WindowState w = mChildren.get(i);
w.onStartFreezingScreen();
}
}
if (forceRotation) {
// The rotation of the real display won't change, so in order to unfreeze the screen
// via {@link #checkAppWindowsReadyToShow}, the windows have to be able to call
// {@link WindowState#reportResized} (it is skipped if the window is freezing) to update
// the drawn state.
return;
}
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
final WindowState w = mChildren.get(i);
w.onStartFreezingScreen();
}
}
@@ -6159,6 +6181,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
}
@Override
void onCancelFixedRotationTransform(int originalDisplayRotation) {
if (this != mDisplayContent.getLastOrientationSource()
|| getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED) {
// Only need to handle the activity that should be rotated with display.
return;
}
// Perform rotation animation according to the rotation of this activity.
startFreezingScreen(originalDisplayRotation);
// This activity may relaunch or perform configuration change so once it has reported drawn,
// the screen can be unfrozen.
ensureActivityConfiguration(0 /* globalChanges */, !PRESERVE_WINDOWS);
}
void setRequestedOrientation(int requestedOrientation) {
setOrientation(requestedOrientation, mayFreezeScreenLocked());
mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(

View File

@@ -495,6 +495,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* The launching activity which is using fixed rotation transformation.
*
* @see #handleTopActivityLaunchingInDifferentOrientation
* @see DisplayRotation#shouldRotateSeamlessly
*/
ActivityRecord mFixedRotationLaunchingApp;
@@ -1238,7 +1239,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (configChanged) {
mWaitingForConfig = true;
mWmService.startFreezingDisplayLocked(0 /* exitAnim */, 0 /* enterAnim */, this);
mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
sendNewConfiguration();
}
@@ -1476,6 +1477,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
sendNewConfiguration();
return true;
}
// The display won't rotate (e.g. the orientation from sensor has updated again before
// applying rotation to display), so clear it to stop using seamless rotation.
mFixedRotationLaunchingApp = null;
return false;
}

View File

@@ -537,8 +537,29 @@ public class DisplayRotation {
}
void prepareNormalRotationAnimation() {
cancelSeamlessRotation();
final RotationAnimationPair anim = selectRotationAnimation();
mService.startFreezingDisplayLocked(anim.mExit, anim.mEnter, mDisplayContent);
mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
}
/**
* This ensures that normal rotation animation is used. E.g. {@link #mRotatingSeamlessly} was
* set by previous {@link #updateRotationUnchecked}, but another orientation change happens
* before calling {@link DisplayContent#sendNewConfiguration} (remote rotation hasn't finished)
* and it doesn't choose seamless rotation.
*/
void cancelSeamlessRotation() {
if (!mRotatingSeamlessly) {
return;
}
mDisplayContent.forAllWindows(w -> {
if (w.mSeamlesslyRotated) {
w.finishSeamlessRotation(false /* timeout */);
w.mSeamlesslyRotated = false;
}
}, true /* traverseTopToBottom */);
mSeamlessRotationCount = 0;
mRotatingSeamlessly = false;
}
private void prepareSeamlessRotation() {

View File

@@ -41,7 +41,6 @@ import android.graphics.Rect;
import android.os.Trace;
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;
@@ -117,8 +116,9 @@ class ScreenRotationAnimation {
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
private int mOriginalRotation;
private int mOriginalWidth, mOriginalHeight;
private final int mOriginalRotation;
private final int mOriginalWidth;
private final int mOriginalHeight;
private int mCurRotation;
private Rect mOriginalDisplayRect = new Rect();
@@ -140,20 +140,18 @@ class ScreenRotationAnimation {
/** Intensity of light/whiteness of the layout after rotation occurs. */
private float mEndLuma;
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
mService = service;
mContext = context;
ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
mService = displayContent.mWmService;
mContext = mService.mContext;
mDisplayContent = displayContent;
displayContent.getBounds(mOriginalDisplayRect);
// Screenshot does NOT include rotation!
final Display display = displayContent.getDisplay();
int originalRotation = display.getRotation();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int realOriginalRotation = displayInfo.rotation;
final int originalWidth;
final int originalHeight;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (fixedToUserRotation) {
if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
// Emulated orientation.
mForceDefaultOrientation = true;
originalWidth = displayContent.mBaseDisplayWidth;
@@ -163,8 +161,8 @@ class ScreenRotationAnimation {
originalWidth = displayInfo.logicalWidth;
originalHeight = displayInfo.logicalHeight;
}
if (originalRotation == Surface.ROTATION_90
|| originalRotation == Surface.ROTATION_270) {
if (realOriginalRotation == Surface.ROTATION_90
|| realOriginalRotation == Surface.ROTATION_270) {
mWidth = originalHeight;
mHeight = originalWidth;
} else {
@@ -173,10 +171,18 @@ class ScreenRotationAnimation {
}
mOriginalRotation = originalRotation;
mOriginalWidth = originalWidth;
mOriginalHeight = originalHeight;
// If the delta is not zero, the rotation of display may not change, but we still want to
// apply rotation animation because there should be a top app shown as rotated. So the
// specified original rotation customizes the direction of animation to have better look
// when restoring the rotated app to the same rotation as current display.
final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
mOriginalWidth = flipped ? originalHeight : originalWidth;
mOriginalHeight = flipped ? originalWidth : originalHeight;
mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();
// Check whether the current screen contains any secure content.
final boolean isSecure = displayContent.hasSecureWindowOnScreen();
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
mBackColorSurface = displayContent.makeChildSurface(null)
@@ -202,7 +208,7 @@ class ScreenRotationAnimation {
t2.apply(true /* sync */);
// Capture a screenshot into the surface we just created.
final int displayId = display.getDisplayId();
final int displayId = displayContent.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
surface.copyFrom(mScreenshotLayer);
SurfaceControl.ScreenshotGraphicBuffer gb =
@@ -242,7 +248,7 @@ class ScreenRotationAnimation {
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" FREEZE %s: CREATE", mScreenshotLayer);
setRotation(t, originalRotation);
setRotation(t, realOriginalRotation);
t.apply();
}

View File

@@ -29,6 +29,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
@@ -2941,7 +2942,7 @@ public class WindowManagerService extends IWindowManager.Stub
mClientFreezingScreen = true;
final long origId = Binder.clearCallingIdentity();
try {
startFreezingDisplayLocked(exitAnim, enterAnim);
startFreezingDisplay(exitAnim, enterAnim);
mH.removeMessages(H.CLIENT_FREEZE_TIMEOUT);
mH.sendEmptyMessageDelayed(H.CLIENT_FREEZE_TIMEOUT, 5000);
} finally {
@@ -5479,13 +5480,17 @@ public class WindowManagerService extends IWindowManager.Stub
return changed;
}
void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
startFreezingDisplayLocked(exitAnim, enterAnim,
getDefaultDisplayContentLocked());
void startFreezingDisplay(int exitAnim, int enterAnim) {
startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());
}
void startFreezingDisplayLocked(int exitAnim, int enterAnim,
DisplayContent displayContent) {
void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {
startFreezingDisplay(exitAnim, enterAnim, displayContent,
ROTATION_UNDEFINED /* overrideOriginalRotation */);
}
void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
int overrideOriginalRotation) {
if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
return;
}
@@ -5529,14 +5534,12 @@ public class WindowManagerService extends IWindowManager.Stub
screenRotationAnimation.kill();
}
// Check whether the current screen contains any secure content.
boolean isSecure = displayContent.hasSecureWindowOnScreen();
displayContent.updateDisplayInfo();
screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
this);
displayContent.setRotationAnimation(screenRotationAnimation);
final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
? overrideOriginalRotation
: displayContent.getDisplayInfo().rotation;
displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
originalRotation));
}
void stopFreezingDisplayLocked() {

View File

@@ -595,7 +595,17 @@ class WindowToken extends WindowContainer<WindowState> {
// The window may be detached or detaching.
return;
}
final int originalRotation = getWindowConfiguration().getRotation();
onConfigurationChanged(parent.getConfiguration());
onCancelFixedRotationTransform(originalRotation);
}
/**
* It is called when the window is using fixed rotation transform, and before display applies
* the same rotation, the rotation change for display is canceled, e.g. the orientation from
* sensor is updated to previous direction.
*/
void onCancelFixedRotationTransform(int originalDisplayRotation) {
}
@Override

View File

@@ -63,6 +63,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
@@ -1272,6 +1273,48 @@ public class ActivityRecordTests extends ActivityTestsBase {
.diff(wpc.getRequestedOverrideConfiguration()));
}
@Test
public void testActivityOnCancelFixedRotationTransform() {
mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
final DisplayRotation displayRotation = mActivity.mDisplayContent.getDisplayRotation();
spyOn(displayRotation);
final DisplayContent display = mActivity.mDisplayContent;
final int originalRotation = display.getRotation();
// Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
mActivity.setVisibility(true);
display.rotateInDifferentOrientationIfNeeded(mActivity);
display.mFixedRotationLaunchingApp = mActivity;
displayRotation.updateRotationUnchecked(false /* forceUpdate */);
assertTrue(displayRotation.isRotatingSeamlessly());
// Simulate the rotation has been updated to previous one, e.g. sensor updates before the
// remote rotation is completed.
doReturn(originalRotation).when(displayRotation).rotationForOrientation(
anyInt() /* orientation */, anyInt() /* lastRotation */);
display.updateOrientation();
final DisplayInfo rotatedInfo = mActivity.getFixedRotationTransformDisplayInfo();
mActivity.finishFixedRotationTransform();
final ScreenRotationAnimation rotationAnim = display.getRotationAnimation();
rotationAnim.setRotation(display.getPendingTransaction(), originalRotation);
// Because the display doesn't rotate, the rotated activity needs to cancel the fixed
// rotation. There should be a rotation animation to cover the change of activity.
verify(mActivity).onCancelFixedRotationTransform(rotatedInfo.rotation);
assertTrue(mActivity.isFreezingScreen());
assertFalse(displayRotation.isRotatingSeamlessly());
assertNotNull(rotationAnim);
assertTrue(rotationAnim.isRotating());
}
@Test
public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
final ActivityRecord secondaryDisplayActivity =