Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f50f8dcb authored by Nick Chameyev's avatar Nick Chameyev
Browse files

Prevent autorotation during hinge movement

Pauses autorotation when hinge angle sensor
reports values and display switch.
This reduces accidental rotation while
folding or unfolding a foldable device.

Bug: 233055896
Test: atest WmTests:DisplayRotationTests
Change-Id: I1b07e1e84ab1cafb0f2794d62d99e114908fe840
parent 6a47399c
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -680,6 +680,20 @@
     rotation. -->
    <bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>

    <!-- Indicates whether the window manager pauses autorotation when folding or unfolding
     a foldable device based on hinge angle sensor events and physical display switch events. -->
    <bool name="config_windowManagerPauseRotationWhenUnfolding">false</bool>

    <!-- Amount of time during which autorotation will be disabled since last hinge angle event -->
    <integer name="config_pauseRotationWhenUnfolding_maxHingeAngle">0</integer>

    <!-- Maximum hinge angle event to be considered to disable autorotation when folding or
     unfolding -->
    <integer name="config_pauseRotationWhenUnfolding_hingeEventTimeout">0</integer>

    <!-- Amount of time during which autorotation will be disabled since last display switch -->
    <integer name="config_pauseRotationWhenUnfolding_displaySwitchTimeout">0</integer>

    <!-- When a device enters any of these states, it should be woken up. States are defined in
         device_state_configuration.xml. -->
    <integer-array name="config_deviceStatesOnWhichToWakeUp">
+4 −0
Original line number Diff line number Diff line
@@ -4017,6 +4017,10 @@
  <java-symbol type="array" name="config_halfFoldedDeviceStates" />
  <java-symbol type="array" name="config_rearDisplayDeviceStates" />
  <java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
  <java-symbol type="bool" name="config_windowManagerPauseRotationWhenUnfolding" />
  <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_hingeEventTimeout" />
  <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_maxHingeAngle" />
  <java-symbol type="integer" name="config_pauseRotationWhenUnfolding_displaySwitchTimeout" />
  <java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
  <java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
  <java-symbol type="string" name="config_foldedArea" />
+2 −1
Original line number Diff line number Diff line
@@ -2921,6 +2921,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                        /* includeRotationSettings */ false);
                mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId,
                        mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
                mDisplayRotation.physicalDisplayChanged();
            }

            // If there is an override set for base values - use it, otherwise use new values.
@@ -3291,7 +3292,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
            handleAnimatingStoppedAndTransition();
            mWmService.stopFreezingDisplayLocked();
            mDisplayRotation.removeDefaultDisplayRotationChangedCallback();
            mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateConsumer);
            super.removeImmediately();
            if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
@@ -3305,6 +3305,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            mWindowingLayer.release();
            mInputMonitor.onDisplayRemoved();
            mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
            mDisplayRotation.onDisplayRemoved();
            mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
            mRootWindowContainer.mTaskSupervisor
                    .getKeyguardController().onDisplayRemoved(mDisplayId);
+182 −1
Original line number Diff line number Diff line
@@ -51,8 +51,13 @@ import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1085,6 +1090,10 @@ public class DisplayRotation {
            return false;
        }

        if (mFoldController != null && mFoldController.shouldDisableRotationSensor()) {
            return false;
        }

        if (mSupportAutoRotation) {
            if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
                    || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -1183,6 +1192,9 @@ public class DisplayRotation {
        int sensorRotation = mOrientationListener != null
                ? mOrientationListener.getProposedRotation() // may be -1
                : -1;
        if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
            sensorRotation = -1;
        }
        if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
            sensorRotation = RotationUtils.reverseRotationDirectionAroundZAxis(sensorRotation);
        }
@@ -1425,6 +1437,11 @@ public class DisplayRotation {
            return false;
        }

        // Do not show rotation choice when fold controller blocks rotation sensor
        if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
            return false;
        }

        // Don't show rotation choice if we are in tabletop or book modes.
        if (isTabletopAutoRotateOverrideEnabled()) return false;

@@ -1527,6 +1544,13 @@ public class DisplayRotation {
        }
    }

    void onDisplayRemoved() {
        removeDefaultDisplayRotationChangedCallback();
        if (mFoldController != null) {
            mFoldController.onDisplayRemoved();
        }
    }

    /** Return whether the rotation settings has changed. */
    private boolean updateSettings() {
        final ContentResolver resolver = mContext.getContentResolver();
@@ -1622,6 +1646,22 @@ public class DisplayRotation {
        pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
        pw.println(prefix + "  mFixedToUserRotation=" + isFixedToUserRotation());

        if (mFoldController != null) {
            pw.println(prefix + "FoldController");
            pw.println(prefix + "  mPauseAutorotationDuringUnfolding="
                    + mFoldController.mPauseAutorotationDuringUnfolding);
            pw.println(prefix + "  mShouldDisableRotationSensor="
                    + mFoldController.mShouldDisableRotationSensor);
            pw.println(prefix + "  mShouldIgnoreSensorRotation="
                    + mFoldController.mShouldIgnoreSensorRotation);
            pw.println(prefix + "  mLastDisplaySwitchTime="
                    + mFoldController.mLastDisplaySwitchTime);
            pw.println(prefix + "  mLastHingeAngleEventTime="
                    + mFoldController.mLastHingeAngleEventTime);
            pw.println(prefix + "  mDeviceState="
                    + mFoldController.mDeviceState);
        }

        if (!mRotationHistory.mRecords.isEmpty()) {
            pw.println();
            pw.println(prefix + "  RotationHistory");
@@ -1663,13 +1703,37 @@ public class DisplayRotation {
        }
    }

    private class FoldController {
    /**
     * Called by the DisplayContent when the physical display changes
     */
    void physicalDisplayChanged() {
        if (mFoldController != null) {
            mFoldController.onPhysicalDisplayChanged();
        }
    }

    @VisibleForTesting
    long uptimeMillis() {
        return SystemClock.uptimeMillis();
    }

    class FoldController {
        private final boolean mPauseAutorotationDuringUnfolding;
        @Surface.Rotation
        private int mHalfFoldSavedRotation = -1; // No saved rotation
        private DeviceStateController.DeviceState mDeviceState =
                DeviceStateController.DeviceState.UNKNOWN;
        private long mLastHingeAngleEventTime = 0;
        private long mLastDisplaySwitchTime = 0;
        private boolean mShouldIgnoreSensorRotation;
        private boolean mShouldDisableRotationSensor;
        private boolean mInHalfFoldTransition = false;
        private int mDisplaySwitchRotationBlockTimeMs;
        private int mHingeAngleRotationBlockTimeMs;
        private int mMaxHingeAngle;
        private final boolean mIsDisplayAlwaysSeparatingHinge;
        private SensorManager mSensorManager;
        private SensorEventListener mHingeAngleSensorEventListener;
        private final Set<Integer> mTabletopRotations;
        private final Runnable mActivityBoundsUpdateCallback;

@@ -1726,6 +1790,48 @@ public class DisplayRotation {
                    }
                }
            };

            mPauseAutorotationDuringUnfolding = mContext.getResources().getBoolean(
                    R.bool.config_windowManagerPauseRotationWhenUnfolding);

            if (mPauseAutorotationDuringUnfolding) {
                mDisplaySwitchRotationBlockTimeMs = mContext.getResources().getInteger(
                        R.integer.config_pauseRotationWhenUnfolding_displaySwitchTimeout);
                mHingeAngleRotationBlockTimeMs = mContext.getResources().getInteger(
                        R.integer.config_pauseRotationWhenUnfolding_hingeEventTimeout);
                mMaxHingeAngle = mContext.getResources().getInteger(
                        R.integer.config_pauseRotationWhenUnfolding_maxHingeAngle);
                registerSensorManager();
            }
        }

        private void registerSensorManager() {
            mSensorManager = mContext.getSystemService(SensorManager.class);
            if (mSensorManager != null) {
                final Sensor hingeAngleSensor = mSensorManager
                        .getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);

                if (hingeAngleSensor != null) {
                    mHingeAngleSensorEventListener = new SensorEventListener() {
                        @Override
                        public void onSensorChanged(SensorEvent event) {
                            onHingeAngleChanged(event.values[0]);
                        }

                        @Override
                        public void onAccuracyChanged(Sensor sensor, int accuracy) {
                        }
                    };
                    mSensorManager.registerListener(mHingeAngleSensorEventListener,
                            hingeAngleSensor, SensorManager.SENSOR_DELAY_FASTEST, getHandler());
                }
            }
        }

        void onDisplayRemoved() {
            if (mSensorManager != null && mHingeAngleSensorEventListener != null) {
                mSensorManager.unregisterListener(mHingeAngleSensorEventListener);
            }
        }

        boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
@@ -1755,6 +1861,7 @@ public class DisplayRotation {
        boolean shouldRevertOverriddenRotation() {
            // When transitioning to open.
            return mDeviceState == DeviceStateController.DeviceState.OPEN
                    && !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
                    && mInHalfFoldTransition
                    && mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
                    && mUserRotationMode
@@ -1801,6 +1908,80 @@ public class DisplayRotation {
            UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback,
                    FOLDING_RECOMPUTE_CONFIG_DELAY_MS);
        }

        boolean shouldIgnoreSensorRotation() {
            return mShouldIgnoreSensorRotation;
        }

        boolean shouldDisableRotationSensor() {
            return mShouldDisableRotationSensor;
        }

        private void updateSensorRotationBlockIfNeeded() {
            final long currentTime = uptimeMillis();
            final boolean newShouldIgnoreRotation =
                    currentTime - mLastDisplaySwitchTime < mDisplaySwitchRotationBlockTimeMs
                    || currentTime - mLastHingeAngleEventTime < mHingeAngleRotationBlockTimeMs;

            if (newShouldIgnoreRotation != mShouldIgnoreSensorRotation) {
                mShouldIgnoreSensorRotation = newShouldIgnoreRotation;

                // Resuming the autorotation
                if (!mShouldIgnoreSensorRotation) {
                    if (mShouldDisableRotationSensor) {
                        // Sensor was disabled, let's re-enable it
                        mShouldDisableRotationSensor = false;
                        updateOrientationListenerLw();
                    } else {
                        // Sensor was not disabled, let's update the rotation in case if we received
                        // some rotation sensor updates when autorotate was disabled
                        updateRotationAndSendNewConfigIfChanged();
                    }
                }
            }
        }

        void onPhysicalDisplayChanged() {
            if (!mPauseAutorotationDuringUnfolding) return;

            mLastDisplaySwitchTime = uptimeMillis();

            final boolean isUnfolding =
                    mDeviceState == DeviceStateController.DeviceState.OPEN
                    || mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;

            if (isUnfolding) {
                // Temporary disable rotation sensor updates when unfolding
                mShouldDisableRotationSensor = true;
                updateOrientationListenerLw();
            }

            updateSensorRotationBlockIfNeeded();
            getHandler().postDelayed(() -> {
                synchronized (mLock) {
                    updateSensorRotationBlockIfNeeded();
                };
            }, mDisplaySwitchRotationBlockTimeMs);
        }

        void onHingeAngleChanged(float hingeAngle) {
            if (hingeAngle < mMaxHingeAngle) {
                mLastHingeAngleEventTime = uptimeMillis();

                updateSensorRotationBlockIfNeeded();

                getHandler().postDelayed(() -> {
                    synchronized (mLock) {
                        updateSensorRotationBlockIfNeeded();
                    };
                }, mHingeAngleRotationBlockTimeMs);
            }
        }
    }

    @VisibleForTesting
    Handler getHandler() {
        return mService.mH;
    }

    private class OrientationListener extends WindowOrientationListener implements Runnable {
+212 −2

File changed.

Preview size limit exceeded, changes collapsed.