summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shivangi Dubey <dshivangi@google.com> 2025-03-07 07:35:08 -0800
committer Shivangi Dubey <dshivangi@google.com> 2025-03-07 07:35:08 -0800
commit4edcbc29adb4f7a8018bc61f691703041c31ba73 (patch)
treef29ce20f0f3c6a9bfdff88c9aa85e50d5de24c92
parent8220a96d49b3e9baf4404f8e5041e1109a71d2a2 (diff)
Create new device state auto rotate setting manager
As part of auto-rotate setting refactor, create a new manager to be used between sysui and settings. This manager will replace the current DeviceStateRotationLockManager. Next CL: Integrate this manager to be used when auto-rotate refactor flag is ON. For more info:go/auto-rotate-refactor Bug: 394303723 Flag: com.android.window.flags.enable_device_state_auto_rotate_setting_refactor Test: atest DeviceStateAutoRotateSettingManagerImplTest Change-Id: I5cd81df5ea602adc3942d869a1294e5ba232d770
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt69
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt191
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt31
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java65
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java2
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt300
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java13
8 files changed, 608 insertions, 64 deletions
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
new file mode 100644
index 000000000000..fdde3d3f5669
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+
+/**
+ * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
+ *
+ * It provides methods to register/unregister listeners for setting changes, update the setting for
+ * specific device states, retrieve the setting value, and check if rotation is locked for specific
+ * or all device states.
+ */
+interface DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
+
+ /** Listener for changes in device-state based auto rotate setting. */
+ interface DeviceStateAutoRotateSettingListener {
+ /** Called whenever the setting has changed. */
+ fun onSettingsChanged()
+ }
+
+ /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
+ fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
+
+ /**
+ * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK] setting.
+ */
+ fun updateSetting(deviceState: Int, autoRotate: Boolean)
+
+ /** Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. */
+ fun getRotationLockSetting(deviceState: Int): Int
+
+ /** Returns true if auto-rotate setting is OFF for [deviceState]. */
+ fun isRotationLocked(deviceState: Int): Boolean
+
+ /** Returns true if the auto-rotate setting value for all device states is OFF. */
+ fun isRotationLockedForAllStates(): Boolean
+
+ /** Returns a list of device states and their respective auto rotate setting availability. */
+ fun getSettableDeviceStates(): List<SettableDeviceState>
+}
+
+/** Represents a device state and whether it has an auto-rotation setting. */
+data class SettableDeviceState(
+ /** Returns the device state associated with this object. */
+ val deviceState: Int,
+ /** Returns whether there is an auto-rotation setting for this device state. */
+ val isSettable: Boolean
+)
+
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
new file mode 100644
index 000000000000..0b6c6e238956
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.Log
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Implementation of [DeviceStateAutoRotateSettingManager]. This implementation is a part of
+ * refactoring, it should be used when [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]
+ * is enabled.
+ */
+class DeviceStateAutoRotateSettingManagerImpl(
+ context: Context,
+ backgroundExecutor: Executor,
+ private val secureSettings: SecureSettings,
+ private val mainHandler: Handler,
+ private val posturesHelper: PosturesHelper,
+) : DeviceStateAutoRotateSettingManager {
+ // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.
+
+ private val settingListeners: MutableList<DeviceStateAutoRotateSettingListener> =
+ mutableListOf()
+ private val fallbackPostureMap = SparseIntArray()
+ private val settableDeviceState: MutableList<SettableDeviceState> = mutableListOf()
+
+ private val autoRotateSettingValue: String
+ get() = secureSettings.getStringForUser(DEVICE_STATE_ROTATION_LOCK, UserHandle.USER_CURRENT)
+
+ init {
+ loadAutoRotateDeviceStates(context)
+ val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean) = notifyListeners()
+ }
+ backgroundExecutor.execute {
+ secureSettings.registerContentObserver(
+ DEVICE_STATE_ROTATION_LOCK, false, contentObserver, UserHandle.USER_CURRENT
+ )
+ }
+ }
+
+ override fun registerListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ settingListeners.add(settingListener)
+ }
+
+ override fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener) {
+ if (!settingListeners.remove(settingListener)) {
+ Log.w(TAG, "Attempting to unregister a listener hadn't been registered")
+ }
+ }
+
+ override fun getRotationLockSetting(deviceState: Int): Int {
+ val devicePosture = posturesHelper.deviceStateToPosture(deviceState)
+ val serializedSetting = autoRotateSettingValue
+ val autoRotateSetting = extractSettingForDevicePosture(devicePosture, serializedSetting)
+
+ // If the setting is ignored for this posture, check the fallback posture.
+ if (autoRotateSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ val fallbackPosture =
+ fallbackPostureMap.get(devicePosture, DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ return extractSettingForDevicePosture(fallbackPosture, serializedSetting)
+ }
+
+ return autoRotateSetting
+ }
+
+ override fun isRotationLocked(deviceState: Int) =
+ getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED
+
+ override fun isRotationLockedForAllStates(): Boolean =
+ convertSerializedSettingToMap(autoRotateSettingValue).all { (_, value) ->
+ value == DEVICE_STATE_ROTATION_LOCK_LOCKED
+ }
+
+ override fun getSettableDeviceStates(): List<SettableDeviceState> = settableDeviceState
+
+ override fun updateSetting(deviceState: Int, autoRotate: Boolean) {
+ // TODO: b/350946537 - Create IPC to update the setting, and call it here.
+ throw UnsupportedOperationException("API updateSetting is not implemented yet")
+ }
+
+ private fun notifyListeners() =
+ settingListeners.forEach { listener -> listener.onSettingsChanged() }
+
+ private fun loadAutoRotateDeviceStates(context: Context) {
+ val perDeviceStateAutoRotateDefaults =
+ context.resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ for (entry in perDeviceStateAutoRotateDefaults) {
+ entry.parsePostureEntry()?.let { (posture, autoRotate, fallbackPosture) ->
+ if (autoRotate == DEVICE_STATE_ROTATION_LOCK_IGNORED && fallbackPosture != null) {
+ fallbackPostureMap.put(posture, fallbackPosture)
+ }
+ settableDeviceState.add(
+ SettableDeviceState(posture, autoRotate != DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ )
+ }
+ }
+ }
+
+ private fun convertSerializedSettingToMap(serializedSetting: String): Map<Int, Int> {
+ if (serializedSetting.isEmpty()) return emptyMap()
+ return try {
+ serializedSetting
+ .split(SEPARATOR_REGEX)
+ .hasEvenSize()
+ .chunked(2)
+ .mapNotNull(::parsePostureSettingPair)
+ .toMap()
+ } catch (e: Exception) {
+ Log.w(
+ TAG,
+ "Invalid format in serializedSetting=$serializedSetting: ${e.message}"
+ )
+ return emptyMap()
+ }
+ }
+
+ private fun List<String>.hasEvenSize(): List<String> {
+ if (this.size % 2 != 0) {
+ throw IllegalStateException("Odd number of elements in the list")
+ }
+ return this
+ }
+
+ private fun parsePostureSettingPair(settingPair: List<String>): Pair<Int, Int>? {
+ return settingPair.let { (keyStr, valueStr) ->
+ val key = keyStr.toIntOrNull()
+ val value = valueStr.toIntOrNull()
+ if (key != null && value != null && value in 0..2) {
+ key to value
+ } else {
+ Log.w(TAG, "Invalid key or value in pair: $keyStr, $valueStr")
+ null // Invalid pair, skip it
+ }
+ }
+ }
+
+ private fun extractSettingForDevicePosture(
+ devicePosture: Int,
+ serializedSetting: String
+ ): Int =
+ convertSerializedSettingToMap(serializedSetting)[devicePosture]
+ ?: DEVICE_STATE_ROTATION_LOCK_IGNORED
+
+ private fun String.parsePostureEntry(): Triple<Int, Int, Int?>? {
+ val values = split(SEPARATOR_REGEX)
+ if (values.size !in 2..3) { // It should contain 2 or 3 values.
+ Log.w(TAG, "Invalid number of values in entry: '$this'")
+ return null
+ }
+ return try {
+ val posture = values[0].toInt()
+ val rotationLockSetting = values[1].toInt()
+ val fallbackPosture = if (values.size == 3) values[2].toIntOrNull() else null
+ Triple(posture, rotationLockSetting, fallbackPosture)
+ } catch (e: NumberFormatException) {
+ Log.w(TAG, "Invalid number format in '$this': ${e.message}")
+ null
+ }
+ }
+
+ companion object {
+ private const val TAG = "DeviceStateAutoRotate"
+ private const val SEPARATOR_REGEX = ":"
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
new file mode 100644
index 000000000000..4d1d29242832
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("DeviceStateAutoRotateSettingUtils")
+
+package com.android.settingslib.devicestate
+
+import android.content.Context
+import com.android.internal.R
+
+/** Returns true if device-state based rotation lock settings are enabled. */
+object DeviceStateAutoRotateSettingUtils {
+ @JvmStatic
+ fun isDeviceStateRotationLockEnabled(context: Context) =
+ context.resources
+ .getStringArray(R.array.config_perDeviceStateRotationLockDefaults)
+ .isNotEmpty()
+}
+
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 635f6905e4f0..deeba574f2ad 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,6 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
+
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -43,7 +45,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Objects;
import java.util.Set;
/**
@@ -58,7 +59,7 @@ public final class DeviceStateRotationLockSettingsManager {
private static DeviceStateRotationLockSettingsManager sSingleton;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
+ private final Set<DeviceStateAutoRotateSettingListener> mListeners = new HashSet<>();
private final SecureSettings mSecureSettings;
private final PosturesHelper mPosturesHelper;
private String[] mPostureRotationLockDefaults;
@@ -127,20 +128,20 @@ public final class DeviceStateRotationLockSettingsManager {
}
/**
- * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings
+ * Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateRotationLockSettingsListener runnable) {
+ public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
/**
- * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance
+ * Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
public void unregisterListener(
- DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) {
- if (!mListeners.remove(deviceStateRotationLockSettingsListener)) {
+ DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
@@ -379,56 +380,8 @@ public final class DeviceStateRotationLockSettingsManager {
}
private void notifyListeners() {
- for (DeviceStateRotationLockSettingsListener r : mListeners) {
+ for (DeviceStateAutoRotateSettingListener r : mListeners) {
r.onSettingsChanged();
}
}
-
- /** Listener for changes in device-state based rotation lock settings */
- public interface DeviceStateRotationLockSettingsListener {
- /** Called whenever the settings have changed. */
- void onSettingsChanged();
- }
-
- /** Represents a device state and whether it has an auto-rotation setting. */
- public static class SettableDeviceState {
- private final int mDeviceState;
- private final boolean mIsSettable;
-
- SettableDeviceState(int deviceState, boolean isSettable) {
- mDeviceState = deviceState;
- mIsSettable = isSettable;
- }
-
- /** Returns the device state associated with this object. */
- public int getDeviceState() {
- return mDeviceState;
- }
-
- /** Returns whether there is an auto-rotation setting for this device state. */
- public boolean isSettable() {
- return mIsSettable;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SettableDeviceState)) return false;
- SettableDeviceState that = (SettableDeviceState) o;
- return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceState, mIsSettable);
- }
-
- @Override
- public String toString() {
- return "SettableDeviceState{"
- + "mDeviceState=" + mDeviceState
- + ", mIsSettable=" + mIsSettable
- + '}';
- }
- }
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 10528739b2b0..ea40e148aed6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
@@ -19,7 +19,7 @@ package com.android.settingslib.devicestate;
import android.database.ContentObserver;
/** Minimal wrapper interface around {@link android.provider.Settings.Secure} for easier testing. */
-interface SecureSettings {
+public interface SecureSettings {
void putStringForUser(String name, String value, int userHandle);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
new file mode 100644
index 000000000000..78dba57028ba
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.devicestate
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.res.Resources
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_HALF_FOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_KEY_UNFOLDED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerImplTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()
+
+ private val fakeSecureSettings = FakeSecureSettings()
+ private val executor: Executor = Executor { it.run() }
+ private val configPerDeviceStateRotationLockDefaults = arrayOf(
+ "$DEVICE_STATE_ROTATION_KEY_HALF_FOLDED:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY:" +
+ "$DEVICE_STATE_ROTATION_LOCK_IGNORED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED",
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED",
+ )
+
+ @Mock
+ private lateinit var mockContext: Context
+
+ @Mock
+ private lateinit var mockContentResolver: ContentResolver
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Mock
+ private lateinit var mockHandler: Handler
+
+ @Mock
+ private lateinit var mockDeviceStateManager: DeviceStateManager
+
+ @Mock
+ private lateinit var mockResources: Resources
+ private lateinit var settingManager: DeviceStateAutoRotateSettingManagerImpl
+
+ @Before
+ fun setUp() {
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+ whenever(mockContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(configPerDeviceStateRotationLockDefaults)
+ whenever(mockHandler.post(any(Runnable::class.java))).thenAnswer { invocation ->
+ val runnable = invocation.arguments[0] as Runnable
+ runnable.run()
+ null
+ }
+ whenever(mockContext.getSystemService(DeviceStateManager::class.java))
+ .thenReturn(mockDeviceStateManager)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_UNFOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_UNFOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_HALF_FOLDED))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_INVALID))
+ .thenReturn(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ whenever(mockPosturesHelper.deviceStateToPosture(DEVICE_STATE_REAR_DISPLAY))
+ .thenReturn(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY)
+
+ settingManager =
+ DeviceStateAutoRotateSettingManagerImpl(
+ mockContext,
+ executor,
+ fakeSecureSettings,
+ mockHandler,
+ mockPosturesHelper,
+ )
+ }
+
+ @Test
+ fun registerListener_onSettingsChanged_listenerNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener).onSettingsChanged()
+ }
+
+ @Test
+ fun registerMultipleListeners_onSettingsChanged_allListenersNotified() {
+ val listener1 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ val listener2 = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener1)
+ settingManager.registerListener(listener2)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener1).onSettingsChanged()
+ verify(listener2).onSettingsChanged()
+ }
+
+ @Test
+ fun unregisterListener_onSettingsChanged_listenerNotNotified() {
+ val listener = mock(DeviceStateAutoRotateSettingListener::class.java)
+ settingManager.registerListener(listener)
+ settingManager.unregisterListener(listener)
+
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ verify(listener, never()).onSettingsChanged()
+ }
+
+ @Test
+ fun getAutoRotateSetting_offForUnfolded_returnsOff() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_onForFolded_returnsOn() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPostureWithNoFallback_returnsIgnored() {
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_INVALID)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidFormat_returnsIgnored() {
+ persistSettings("invalid_format")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_invalidNumberFormat_returnsIgnored() {
+ persistSettings("$DEVICE_STATE_ROTATION_KEY_FOLDED:4")
+
+ val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+
+ assertThat(autoRotateSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_IGNORED)
+ }
+
+ @Test
+ fun getAutoRotateSetting_multipleSettings_returnsCorrectSetting() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val foldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_FOLDED)
+ val unfoldedSetting = settingManager.getRotationLockSetting(DEVICE_STATE_UNFOLDED)
+
+ assertThat(foldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ assertThat(unfoldedSetting).isEqualTo(DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
+ }
+
+ @Test
+ fun isAutoRotateOff_offForUnfolded_returnsTrue() {
+ persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+
+ val isAutoRotateOff = settingManager.isRotationLocked(DEVICE_STATE_UNFOLDED)
+
+ assertThat(isAutoRotateOff).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_allStatesLocked_returnsTrue() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isTrue()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_someStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun isRotationLockedForAllStates_noStatesLocked_returnsFalse() {
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
+
+ val isRotationLockedForAllStates = settingManager.isRotationLockedForAllStates()
+
+ assertThat(isRotationLockedForAllStates).isFalse()
+ }
+
+ @Test
+ fun getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
+ val settableDeviceStates = settingManager.getSettableDeviceStates()
+
+ assertThat(settableDeviceStates)
+ .containsExactly(
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_UNFOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
+ SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
+ )
+ }
+
+ private fun persistSettings(devicePosture: Int, autoRotateSetting: Int) {
+ persistSettings("$devicePosture:$autoRotateSetting")
+ }
+
+ private fun persistSettings(value: String) {
+ fakeSecureSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK, value, UserHandle.USER_CURRENT
+ )
+ }
+
+ private companion object {
+ const val DEVICE_STATE_FOLDED = 0
+ const val DEVICE_STATE_HALF_FOLDED = 1
+ const val DEVICE_STATE_UNFOLDED = 2
+ const val DEVICE_STATE_REAR_DISPLAY = 3
+ const val DEVICE_STATE_INVALID = 4
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index 9f9aaf5ff83a..baebaf7dfef0 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -40,7 +40,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
import com.google.common.truth.Expect;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index b13e01be40f7..fa022b4768fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -27,6 +27,7 @@ import android.util.IndentingPrintWriter;
import androidx.annotation.NonNull;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
@@ -56,8 +57,8 @@ public final class DeviceStateRotationLockSettingController
private int mDeviceState = -1;
@Nullable
private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
- private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
- mDeviceStateRotationLockSettingsListener;
+ private DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
+ mDeviceStateAutoRotateSettingListener;
@Inject
public DeviceStateRotationLockSettingController(
@@ -83,17 +84,17 @@ public final class DeviceStateRotationLockSettingController
// is no user action.
mDeviceStateCallback = this::updateDeviceState;
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
- mDeviceStateRotationLockSettingsListener = () ->
+ mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
mDeviceStateRotationLockSettingsManager.registerListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
- if (mDeviceStateRotationLockSettingsListener != null) {
+ if (mDeviceStateAutoRotateSettingListener != null) {
mDeviceStateRotationLockSettingsManager.unregisterListener(
- mDeviceStateRotationLockSettingsListener);
+ mDeviceStateAutoRotateSettingListener);
}
}
}