Merge "Apply rotation animation to restore rotated activity" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
45e7091f37
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user