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

Commit d41d1a69 authored by Christian Göllner's avatar Christian Göllner
Browse files

Device state based auto-rotation setting: add fallback setting mechanism

This is required so that when the device goes into a state where the setting is ignored (e.g. half-folded), it falls back to the setting that is specified in the config, instead of simply the last state that was set.

This helps apps that read the auto rotation setting to make decision changes to get the correct value.

Bug: 203189552
Test: DeviceStateRotationLockSettingControllerTest
Change-Id: I3c6b006dc7ffdcfff62798558f09599f63a9f70a
parent 09e9e3b3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -127,10 +127,12 @@ public final class DeviceStateRotationLockSettingController
        int rotationLockSetting =
                mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
            // This should not happen. Device states that have an ignored setting, should also
            // specify a fallback device state which is not ignored.
            // We won't handle this device state. The same rotation lock setting as before should
            // apply and any changes to the rotation lock setting will be written for the previous
            // valid device state.
            Log.v(TAG, "Ignoring new device state: " + state);
            Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
            return;
        }

+54 −17
Original line number Diff line number Diff line
@@ -52,12 +52,14 @@ public final class DeviceStateRotationLockSettingsManager {
    private final Handler mMainHandler = Handler.getMain();
    private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
    private SparseIntArray mDeviceStateRotationLockSettings;
    private SparseIntArray mDeviceStateRotationLockFallbackSettings;

    private DeviceStateRotationLockSettingsManager(Context context) {
        mContentResolver = context.getContentResolver();
        mDeviceStateRotationLockDefaults =
                context.getResources()
                        .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
        loadDefaults();
        initializeInMemoryMap();
        listenForSettingsChange(context);
    }
@@ -114,6 +116,11 @@ public final class DeviceStateRotationLockSettingsManager {

    /** Updates the rotation lock setting for a specified device state. */
    public void updateSetting(int deviceState, boolean rotationLocked) {
        if (mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState) >= 0) {
            // The setting for this device state is IGNORED, and has a fallback device state.
            // The setting for that fallback device state should be the changed in this case.
            deviceState = mDeviceStateRotationLockFallbackSettings.get(deviceState);
        }
        mDeviceStateRotationLockSettings.put(
                deviceState,
                rotationLocked
@@ -123,16 +130,37 @@ public final class DeviceStateRotationLockSettingsManager {
    }

    /**
     * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting
     * is specified for this device state, it will return {@link
     * Returns the {@link Settings.Secure.DeviceStateRotationLockSetting} for the given device
     * state.
     *
     * <p>If the setting for this device state is {@link DEVICE_STATE_ROTATION_LOCK_IGNORED}, it
     * will return the setting for the fallback device state.
     *
     * <p>If no fallback is specified for this device state, it will return {@link
     * DEVICE_STATE_ROTATION_LOCK_IGNORED}.
     */
    @Settings.Secure.DeviceStateRotationLockSetting
    public int getRotationLockSetting(int deviceState) {
        return mDeviceStateRotationLockSettings.get(
                deviceState, DEVICE_STATE_ROTATION_LOCK_IGNORED);
        int rotationLockSetting = mDeviceStateRotationLockSettings.get(
                deviceState, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
            rotationLockSetting = getFallbackRotationLockSetting(deviceState);
        }
        return rotationLockSetting;
    }

    private int getFallbackRotationLockSetting(int deviceState) {
        int indexOfFallbackState = mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState);
        if (indexOfFallbackState < 0) {
            Log.w(TAG, "Setting is ignored, but no fallback was specified.");
            return DEVICE_STATE_ROTATION_LOCK_IGNORED;
        }
        int fallbackState = mDeviceStateRotationLockFallbackSettings.valueAt(indexOfFallbackState);
        return mDeviceStateRotationLockSettings.get(fallbackState,
                /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
    }


    /** Returns true if the rotation is locked for the current device state */
    public boolean isRotationLocked(int deviceState) {
        return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
@@ -223,21 +251,30 @@ public final class DeviceStateRotationLockSettingsManager {
    }

    private void loadDefaults() {
        if (mDeviceStateRotationLockDefaults.length == 0) {
            Log.w(TAG, "Empty default settings");
            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */ 0);
            return;
        }
        mDeviceStateRotationLockSettings =
                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
        mDeviceStateRotationLockSettings = new SparseIntArray(
                mDeviceStateRotationLockDefaults.length);
        mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
        for (String entry : mDeviceStateRotationLockDefaults) {
            String[] values = entry.split(SEPARATOR_REGEX);
            try {
                int key = Integer.parseInt(entry[0]);
                int value = Integer.parseInt(entry[1]);
                mDeviceStateRotationLockSettings.put(key, value);
                int deviceState = Integer.parseInt(values[0]);
                int rotationLockSetting = Integer.parseInt(values[1]);
                if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
                    if (values.length == 3) {
                        int fallbackDeviceState = Integer.parseInt(values[2]);
                        mDeviceStateRotationLockFallbackSettings.put(deviceState,
                                fallbackDeviceState);
                    } else {
                        Log.w(TAG,
                                "Rotation lock setting is IGNORED, but values have unexpected "
                                        + "size of "
                                        + values.length);
                    }
                }
                mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
            } catch (NumberFormatException e) {
                Log.wtf(TAG, "Error deserializing default settings", e);
                Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
                return;
            }
        }
    }
+102 −10
Original line number Diff line number Diff line
@@ -52,13 +52,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest
public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {

    private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
    private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};

    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
    private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
    @Mock DeviceStateManager mDeviceStateManager;
    RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
    DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
    @Mock
    private DeviceStateManager mDeviceStateManager;
    private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
    private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
    private DeviceStateRotationLockSettingsManager mSettingsManager;
    private TestableContentResolver mContentResolver;
@@ -93,7 +94,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
                                mContentResolver,
                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                                UserHandle.USER_CURRENT))
                .isEqualTo("0:0:1:2");
                .isEqualTo("0:1:1:2:2:0");
    }

    @Test
@@ -124,6 +125,31 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
    }

    @Test
    public void whenDeviceStateSwitched_settingIsIgnored_loadsDefaultFallbackSetting() {
        initializeSettingsWith();
        mFakeRotationPolicy.setRotationLock(true);

        // State 2 -> Ignored -> Fall back to state 1 which is unlocked
        mDeviceStateCallback.onStateChanged(2);

        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
    }

    @Test
    public void whenDeviceStateSwitched_ignoredSetting_fallbackValueChanges_usesFallbackValue() {
        initializeSettingsWith(
                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                1, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
        mFakeRotationPolicy.setRotationLock(false);

        // State 2 -> Ignored -> Fall back to state 1 which is locked
        mDeviceStateCallback.onStateChanged(2);

        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
    }

    @Test
    public void whenUserChangesSetting_saveSettingForCurrentState() {
        initializeSettingsWith(
@@ -159,15 +185,15 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
    }

    @Test
    public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
    public void whenDeviceStateSwitchedToIgnoredState_noFallback_newSettingsSaveForPreviousState() {
        initializeSettingsWith(
                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
                8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
        mFakeRotationPolicy.setRotationLock(true);

        mDeviceStateCallback.onStateChanged(1);
        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();

        mDeviceStateCallback.onStateChanged(0);
        mDeviceStateCallback.onStateChanged(8);
        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();

        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -178,7 +204,7 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
                                mContentResolver,
                                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                                UserHandle.USER_CURRENT))
                .isEqualTo("0:0:1:1");
                .isEqualTo("1:1:8:0");
    }

    @Test
@@ -198,12 +224,78 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
    }

    @Test
    public void onRotationLockStateChanged_newSettingIsPersisted() {
        initializeSettingsWith(
                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
        mDeviceStateCallback.onStateChanged(0);

        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                /* rotationLocked= */ false,
                /* affordanceVisible= */ true
        );

        assertThat(
                Settings.Secure.getStringForUser(
                        mContentResolver,
                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                        UserHandle.USER_CURRENT))
                .isEqualTo("0:2:1:2");
    }

    @Test
    public void onRotationLockStateChanged_deviceStateIsIgnored_newSettingIsPersistedToFallback() {
        initializeSettingsWith(
                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
        mDeviceStateCallback.onStateChanged(2);

        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                /* rotationLocked= */ true,
                /* affordanceVisible= */ true
        );

        assertThat(
                Settings.Secure.getStringForUser(
                        mContentResolver,
                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                        UserHandle.USER_CURRENT))
                .isEqualTo("0:1:1:1:2:0");
    }

    @Test
    public void onRotationLockStateChange_stateIgnored_noFallback_settingIsPersistedToPrevious() {
        initializeSettingsWith(
                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
                8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
        mDeviceStateCallback.onStateChanged(1);
        mDeviceStateCallback.onStateChanged(8);

        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
                /* rotationLocked= */ true,
                /* affordanceVisible= */ true
        );

        assertThat(
                Settings.Secure.getStringForUser(
                        mContentResolver,
                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                        UserHandle.USER_CURRENT))
                .isEqualTo("0:1:1:1:8:0");
    }

    private void initializeSettingsWith(int... values) {
        if (values.length % 2 != 0) {
            throw new IllegalArgumentException("Expecting key-value pairs");
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < values.length; sb.append(":")) {
        for (int i = 0; i < values.length; ) {
            if (i > 0) {
                sb.append(":");
            }
            sb.append(values[i++]).append(":").append(values[i++]);
        }