diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d4b151dd82201..73518dc23379e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -984,6 +984,12 @@ true + + + + + + diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java index 2e67399321a00..7d9a4ec4b5c33 100644 --- a/services/core/java/com/android/server/wm/DeviceStateController.java +++ b/services/core/java/com/android/server/wm/DeviceStateController.java @@ -47,10 +47,13 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb @NonNull private final int[] mRearDisplayDeviceStates; @NonNull + private final int[] mReverseRotationAroundZAxisStates; + @NonNull private final List> mDeviceStateCallbacks = new ArrayList<>(); @Nullable private DeviceState mLastDeviceState; + private int mCurrentState; public enum DeviceState { UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR, @@ -58,6 +61,7 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb DeviceStateController(@NonNull Context context, @NonNull Handler handler) { mDeviceStateManager = context.getSystemService(DeviceStateManager.class); + mOpenDeviceStates = context.getResources() .getIntArray(R.array.config_openDeviceStates); mHalfFoldedDeviceStates = context.getResources() @@ -66,6 +70,8 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb .getIntArray(R.array.config_foldedDeviceStates); mRearDisplayDeviceStates = context.getResources() .getIntArray(R.array.config_rearDisplayDeviceStates); + mReverseRotationAroundZAxisStates = context.getResources() + .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis); if (mDeviceStateManager != null) { mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this); @@ -82,8 +88,17 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb mDeviceStateCallbacks.add(callback); } + /** + * @return true if the rotation direction on the Z axis should be reversed. + */ + boolean shouldReverseRotationDirectionAroundZAxis() { + return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState); + } + @Override public void onStateChanged(int state) { + mCurrentState = state; + final DeviceState deviceState; if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) { deviceState = DeviceState.HALF_FOLDED; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5b2a3e33d01fe..3b898d59cc539 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1128,7 +1128,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH); mDisplayPolicy = new DisplayPolicy(mWmService, this); - mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address); + mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address, + mDeviceStateController); final Consumer deviceStateConsumer = (@NonNull DeviceStateController.DeviceState newFoldState) -> { diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index a1e18cf75ae60..3f61315ccc643 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -41,6 +41,7 @@ import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_D import android.annotation.AnimRes; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.ContentResolver; @@ -117,6 +118,8 @@ public class DisplayRotation { private SettingsObserver mSettingsObserver; @Nullable private FoldController mFoldController; + @NonNull + private final DeviceStateController mDeviceStateController; @ScreenOrientation private int mCurrentAppOrientation = SCREEN_ORIENTATION_UNSPECIFIED; @@ -218,21 +221,24 @@ public class DisplayRotation { private boolean mDemoRotationLock; DisplayRotation(WindowManagerService service, DisplayContent displayContent, - DisplayAddress displayAddress) { + DisplayAddress displayAddress, @NonNull DeviceStateController deviceStateController) { this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(), - service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); + service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock(), + deviceStateController); } @VisibleForTesting DisplayRotation(WindowManagerService service, DisplayContent displayContent, DisplayAddress displayAddress, DisplayPolicy displayPolicy, - DisplayWindowSettings displayWindowSettings, Context context, Object lock) { + DisplayWindowSettings displayWindowSettings, Context context, Object lock, + @NonNull DeviceStateController deviceStateController) { mService = service; mDisplayContent = displayContent; mDisplayPolicy = displayPolicy; mDisplayWindowSettings = displayWindowSettings; mContext = context; mLock = lock; + mDeviceStateController = deviceStateController; isDefaultDisplay = displayContent.isDefaultDisplay; mCompatPolicyForImmersiveApps = initImmersiveAppCompatPolicy(service, displayContent); @@ -1137,6 +1143,15 @@ public class DisplayRotation { int sensorRotation = mOrientationListener != null ? mOrientationListener.getProposedRotation() // may be -1 : -1; + if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) { + // Flipping 270 and 90 has the same effect as changing the direction which rotation is + // applied. + if (sensorRotation == Surface.ROTATION_90) { + sensorRotation = Surface.ROTATION_270; + } else if (sensorRotation == Surface.ROTATION_270) { + sensorRotation = Surface.ROTATION_90; + } + } mLastSensorRotation = sensorRotation; if (sensorRotation < 0) { sensorRotation = lastRotation; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index f814608ed87ae..ed2b0a36cd5c8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -56,6 +56,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.devicestate.DeviceStateManager; import android.os.PowerManagerInternal; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; @@ -111,6 +112,7 @@ public class DisplayRotationTests { private ContentResolver mMockResolver; private FakeSettingsProvider mFakeSettingsProvider; private StatusBarManagerInternal mMockStatusBarManagerInternal; + private DeviceStateManager mMockDeviceStateManager; // Fields below are callbacks captured from test target. private ContentObserver mShowRotationSuggestionsObserver; @@ -120,6 +122,7 @@ public class DisplayRotationTests { private DisplayRotationBuilder mBuilder; + private DeviceStateController mDeviceStateController; private DisplayRotation mTarget; @BeforeClass @@ -484,6 +487,34 @@ public class DisplayRotationTests { SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); } + @Test + public void testReverseRotation() throws Exception { + mBuilder.build(); + configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); + + when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true); + + thawRotation(); + + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90)); + assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_270)); + assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0)); + assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0)); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation( + SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_180)); + } + private boolean waitForUiHandler() { final CountDownLatch latch = new CountDownLatch(1); UiThread.getHandler().post(latch::countDown); @@ -1097,8 +1128,14 @@ public class DisplayRotationTests { mMockDisplayWindowSettings = mock(DisplayWindowSettings.class); + mMockDeviceStateManager = mock(DeviceStateManager.class); + when(mMockContext.getSystemService(eq(DeviceStateManager.class))) + .thenReturn(mMockDeviceStateManager); + + mDeviceStateController = mock(DeviceStateController.class); mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress, - mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object()) { + mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object(), + mDeviceStateController) { @Override DisplayRotationImmersiveAppCompatPolicy initImmersiveAppCompatPolicy( WindowManagerService service, DisplayContent displayContent) {