From 4831a179cb2290e7d6f60baddf369914e08f67b1 Mon Sep 17 00:00:00 2001 From: Alex Florescu Date: Wed, 27 Oct 2021 16:51:02 +0100 Subject: [PATCH] Extract device state rotation lock settings management to a separate class. This will be followed-up by a CL moving it to SettingsLib so it can be shared with Settings Bug: 195757480 Test: atest SystemUITests:DeviceStateRotationLockSettingControllerTest Change-Id: Ic9c720d7c911d9b8db70ea6bb7fbab209a602abe --- core/java/android/provider/Settings.java | 2 +- ...iceStateRotationLockSettingController.java | 165 +++-------- ...eviceStateRotationLockSettingsManager.java | 266 ++++++++++++++++++ .../policy/RotationLockControllerImpl.java | 1 - .../policy/dagger/StatusBarPolicyModule.java | 10 + ...tateRotationLockSettingControllerTest.java | 151 +++++----- .../RotationLockControllerImplTest.java | 28 +- 7 files changed, 406 insertions(+), 217 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 72e28630da40..5cdb1fdf64ce 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10525,7 +10525,7 @@ public final class Settings { DEVICE_STATE_ROTATION_LOCK_UNLOCKED, }) @Retention(RetentionPolicy.SOURCE) - @interface DeviceStateRotationLockSetting { + public @interface DeviceStateRotationLockSetting { } /** 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 41cacf5142fd..1030bfdb40fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java @@ -16,110 +16,53 @@ package com.android.systemui.statusbar.policy; - import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED; 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.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS; import android.annotation.Nullable; import android.hardware.devicestate.DeviceStateManager; -import android.os.UserHandle; -import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; -import android.util.SparseIntArray; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.wrapper.RotationPolicyWrapper; import java.util.concurrent.Executor; import javax.inject.Inject; -import javax.inject.Named; /** - * Handles reading and writing of rotation lock settings per device state, as well as setting - * the rotation lock when device state changes. - **/ + * Handles reading and writing of rotation lock settings per device state, as well as setting the + * rotation lock when device state changes. + */ @SysUISingleton -public final class DeviceStateRotationLockSettingController implements Listenable, - RotationLockController.RotationLockControllerCallback { +public final class DeviceStateRotationLockSettingController + implements Listenable, RotationLockController.RotationLockControllerCallback { private static final String TAG = "DSRotateLockSettingCon"; - private static final String SEPARATOR_REGEX = ":"; - - private final SecureSettings mSecureSettings; private final RotationPolicyWrapper mRotationPolicyWrapper; private final DeviceStateManager mDeviceStateManager; private final Executor mMainExecutor; - private final String[] mDeviceStateRotationLockDefaults; + private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager; - private SparseIntArray mDeviceStateRotationLockSettings; - // TODO(b/183001527): Add API to query current device state and initialize this. + // On registration for DeviceStateCallback, we will receive a callback with the current state + // and this will be initialized. private int mDeviceState = -1; - @Nullable - private DeviceStateManager.DeviceStateCallback mDeviceStateCallback; - + @Nullable private DeviceStateManager.DeviceStateCallback mDeviceStateCallback; + private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener + mDeviceStateRotationLockSettingsListener; @Inject public DeviceStateRotationLockSettingController( - SecureSettings secureSettings, RotationPolicyWrapper rotationPolicyWrapper, DeviceStateManager deviceStateManager, @Main Executor executor, - @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults - ) { - mSecureSettings = secureSettings; + DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager) { mRotationPolicyWrapper = rotationPolicyWrapper; mDeviceStateManager = deviceStateManager; mMainExecutor = executor; - mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults; - } - - /** - * Loads the settings from storage. - */ - public void initialize() { - String serializedSetting = - mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - UserHandle.USER_CURRENT); - if (TextUtils.isEmpty(serializedSetting)) { - // No settings saved, we should load the defaults and persist them. - fallbackOnDefaults(); - return; - } - String[] values = serializedSetting.split(SEPARATOR_REGEX); - if (values.length % 2 != 0) { - // Each entry should be a key/value pair, so this is corrupt. - Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults"); - fallbackOnDefaults(); - return; - } - mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2); - int key; - int value; - - for (int i = 0; i < values.length - 1; ) { - try { - key = Integer.parseInt(values[i++]); - value = Integer.parseInt(values[i++]); - mDeviceStateRotationLockSettings.put(key, value); - } catch (NumberFormatException e) { - Log.wtf(TAG, "Error deserializing one of the saved settings", e); - fallbackOnDefaults(); - return; - } - } - } - - private void fallbackOnDefaults() { - loadDefaults(); - persistSettings(); + mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager; } @Override @@ -129,10 +72,17 @@ public final class DeviceStateRotationLockSettingController implements Listenabl // is no user action. mDeviceStateCallback = this::updateDeviceState; mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback); + mDeviceStateRotationLockSettingsListener = () -> readPersistedSetting(mDeviceState); + mDeviceStateRotationLockSettingsManager.registerListener( + mDeviceStateRotationLockSettingsListener); } else { if (mDeviceStateCallback != null) { mDeviceStateManager.unregisterCallback(mDeviceStateCallback); } + if (mDeviceStateRotationLockSettingsListener != null) { + mDeviceStateRotationLockSettingsManager.unregisterListener( + mDeviceStateRotationLockSettingsListener); + } } } @@ -143,7 +93,8 @@ public final class DeviceStateRotationLockSettingController implements Listenabl return; } - if (rotationLocked == isRotationLockedForCurrentState()) { + if (rotationLocked + == mDeviceStateRotationLockSettingsManager.isRotationLocked(mDeviceState)) { Log.v(TAG, "Rotation lock same as the current setting, no need to update."); return; } @@ -152,19 +103,15 @@ public final class DeviceStateRotationLockSettingController implements Listenabl } private void saveNewRotationLockSetting(boolean isRotationLocked) { - Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked=" - + isRotationLocked + "]"); + Log.v( + TAG, + "saveNewRotationLockSetting [state=" + + mDeviceState + + "] [isRotationLocked=" + + isRotationLocked + + "]"); - mDeviceStateRotationLockSettings.put(mDeviceState, - isRotationLocked - ? DEVICE_STATE_ROTATION_LOCK_LOCKED - : DEVICE_STATE_ROTATION_LOCK_UNLOCKED); - persistSettings(); - } - - private boolean isRotationLockedForCurrentState() { - return mDeviceStateRotationLockSettings.get(mDeviceState, - DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED; + mDeviceStateRotationLockSettingsManager.updateSetting(mDeviceState, isRotationLocked); } private void updateDeviceState(int state) { @@ -173,8 +120,12 @@ public final class DeviceStateRotationLockSettingController implements Listenabl return; } + readPersistedSetting(state); + } + + private void readPersistedSetting(int state) { int rotationLockSetting = - mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED); + mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state); if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_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 @@ -186,54 +137,10 @@ public final class DeviceStateRotationLockSettingController implements Listenabl // Accept the new state mDeviceState = state; - // Update the rotation lock setting if needed for this new device state + // Update the rotation policy, if needed, for this new device state boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED; if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) { mRotationPolicyWrapper.setRotationLock(newRotationLockSetting); } } - - private void persistSettings() { - if (mDeviceStateRotationLockSettings.size() == 0) { - mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"", UserHandle.USER_CURRENT); - return; - } - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0)) - .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.valueAt(0)); - - for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) { - stringBuilder - .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.keyAt(i)) - .append(SEPARATOR_REGEX) - .append(mDeviceStateRotationLockSettings.valueAt(i)); - } - mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - stringBuilder.toString(), UserHandle.USER_CURRENT); - } - - 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); - try { - int key = Integer.parseInt(entry[0]); - int value = Integer.parseInt(entry[1]); - mDeviceStateRotationLockSettings.put(key, value); - } catch (NumberFormatException e) { - Log.wtf(TAG, "Error deserializing default settings", e); - } - } - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java new file mode 100644 index 000000000000..a418c74848a5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2022 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.systemui.statusbar.policy; + +import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED; +import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED; +import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseIntArray; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.HashSet; +import java.util.Set; + +/** + * Manages device-state based rotation lock settings. Handles reading, writing, and listening for + * changes. + */ +public final class DeviceStateRotationLockSettingsManager { + + private static final String TAG = "DSRotLockSettingsMngr"; + private static final String SEPARATOR_REGEX = ":"; + + private static DeviceStateRotationLockSettingsManager sSingleton; + + private final ContentResolver mContentResolver; + private final String[] mDeviceStateRotationLockDefaults; + private final Handler mMainHandler = Handler.getMain(); + private final Set mListeners = new HashSet<>(); + private SparseIntArray mDeviceStateRotationLockSettings; + + private DeviceStateRotationLockSettingsManager(Context context) { + mContentResolver = context.getContentResolver(); + mDeviceStateRotationLockDefaults = + context.getResources() + .getStringArray(R.array.config_perDeviceStateRotationLockDefaults); + initializeInMemoryMap(); + listenForSettingsChange(context); + } + + /** Returns a singleton instance of this class */ + public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) { + if (sSingleton == null) { + sSingleton = + new DeviceStateRotationLockSettingsManager(context.getApplicationContext()); + } + return sSingleton; + } + + /** Returns true if device-state based rotation lock settings are enabled. */ + public static boolean isDeviceStateRotationLockEnabled(Context context) { + return context.getResources() + .getStringArray(R.array.config_perDeviceStateRotationLockDefaults) + .length + > 0; + } + + private void listenForSettingsChange(Context context) { + context.getContentResolver() + .registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.DEVICE_STATE_ROTATION_LOCK), + /* notifyForDescendents= */ false, //NOTYPO + new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + onPersistedSettingsChanged(); + } + }, + UserHandle.USER_CURRENT); + } + + /** + * Registers a {@link DeviceStateRotationLockSettingsListener} to be notified when the settings + * change. Can be called multiple times with different listeners. + */ + public void registerListener(DeviceStateRotationLockSettingsListener runnable) { + mListeners.add(runnable); + } + + /** + * Unregisters a {@link DeviceStateRotationLockSettingsListener}. No-op if the given instance + * was never registered. + */ + public void unregisterListener( + DeviceStateRotationLockSettingsListener deviceStateRotationLockSettingsListener) { + if (!mListeners.remove(deviceStateRotationLockSettingsListener)) { + Log.w(TAG, "Attempting to unregister a listener hadn't been registered"); + } + } + + /** Updates the rotation lock setting for a specified device state. */ + public void updateSetting(int deviceState, boolean rotationLocked) { + mDeviceStateRotationLockSettings.put( + deviceState, + rotationLocked + ? DEVICE_STATE_ROTATION_LOCK_LOCKED + : DEVICE_STATE_ROTATION_LOCK_UNLOCKED); + persistSettings(); + } + + /** + * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting + * 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); + } + + /** 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; + } + + /** + * Returns true if there is no device state for which the current setting is {@link + * DEVICE_STATE_ROTATION_LOCK_UNLOCKED}. + */ + public boolean isRotationLockedForAllStates() { + for (int i = 0; i < mDeviceStateRotationLockSettings.size(); i++) { + if (mDeviceStateRotationLockSettings.valueAt(i) + == DEVICE_STATE_ROTATION_LOCK_UNLOCKED) { + return false; + } + } + return true; + } + + private void initializeInMemoryMap() { + String serializedSetting = + Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + UserHandle.USER_CURRENT); + if (TextUtils.isEmpty(serializedSetting)) { + // No settings saved, we should load the defaults and persist them. + fallbackOnDefaults(); + return; + } + String[] values = serializedSetting.split(SEPARATOR_REGEX); + if (values.length % 2 != 0) { + // Each entry should be a key/value pair, so this is corrupt. + Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults"); + fallbackOnDefaults(); + return; + } + mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2); + int key; + int value; + + for (int i = 0; i < values.length - 1; ) { + try { + key = Integer.parseInt(values[i++]); + value = Integer.parseInt(values[i++]); + mDeviceStateRotationLockSettings.put(key, value); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Error deserializing one of the saved settings", e); + fallbackOnDefaults(); + return; + } + } + } + + private void fallbackOnDefaults() { + loadDefaults(); + persistSettings(); + } + + private void persistSettings() { + if (mDeviceStateRotationLockSettings.size() == 0) { + Settings.Secure.putStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + /* value= */ "", + UserHandle.USER_CURRENT); + return; + } + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder + .append(mDeviceStateRotationLockSettings.keyAt(0)) + .append(SEPARATOR_REGEX) + .append(mDeviceStateRotationLockSettings.valueAt(0)); + + for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) { + stringBuilder + .append(SEPARATOR_REGEX) + .append(mDeviceStateRotationLockSettings.keyAt(i)) + .append(SEPARATOR_REGEX) + .append(mDeviceStateRotationLockSettings.valueAt(i)); + } + Settings.Secure.putStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + stringBuilder.toString(), + UserHandle.USER_CURRENT); + } + + 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); + try { + int key = Integer.parseInt(entry[0]); + int value = Integer.parseInt(entry[1]); + mDeviceStateRotationLockSettings.put(key, value); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Error deserializing default settings", e); + } + } + } + + /** + * Called when the persisted settings have changed, requiring a reinitialization of the + * in-memory map. + */ + @VisibleForTesting + public void onPersistedSettingsChanged() { + initializeInMemoryMap(); + notifyListeners(); + } + + private void notifyListeners() { + for (DeviceStateRotationLockSettingsListener 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(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 3143a471649c..1eeb0ac8b3bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -64,7 +64,6 @@ public final class RotationLockControllerImpl implements RotationLockController mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController; mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0; if (mIsPerDeviceStateRotationLockEnabled) { - deviceStateRotationLockSettingController.initialize(); mCallbacks.add(mDeviceStateRotationLockSettingController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index b6a96a7e49b9..60938fb2feb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy.dagger; +import android.content.Context; import android.content.res.Resources; import android.os.UserManager; @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.policy.DeviceControlsController; import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.DevicePostureControllerImpl; +import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingsManager; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionControllerImpl; import com.android.systemui.statusbar.policy.FlashlightController; @@ -163,6 +165,14 @@ public interface StatusBarPolicyModule { return controller; } + /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */ + @SysUISingleton + @Provides + static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager( + Context context) { + return DeviceStateRotationLockSettingsManager.getInstance(context); + } + /** * Default values for per-device state rotation lock settings. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java index db7b2f20fa4c..a8522c787029 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java @@ -16,6 +16,10 @@ package com.android.systemui.statusbar.policy; +import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED; +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.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -25,14 +29,15 @@ import android.hardware.devicestate.DeviceStateManager; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableContentResolver; import android.testing.TestableResources; import androidx.test.filters.SmallTest; +import com.android.internal.R; import com.android.internal.view.RotationPolicy; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wrapper.RotationPolicyWrapper; @@ -47,63 +52,55 @@ 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:0", "1:2"}; - private final FakeSettings mFakeSettings = new FakeSettings(); private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock); @Mock DeviceStateManager mDeviceStateManager; RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy(); DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; private DeviceStateManager.DeviceStateCallback mDeviceStateCallback; + private DeviceStateRotationLockSettingsManager mSettingsManager; + private TestableContentResolver mContentResolver; @Before public void setUp() { MockitoAnnotations.initMocks(/* testClass= */ this); TestableResources resources = mContext.getOrCreateTestableResources(); + resources.addOverride(R.array.config_perDeviceStateRotationLockDefaults, DEFAULT_SETTINGS); ArgumentCaptor deviceStateCallbackArgumentCaptor = - ArgumentCaptor.forClass( - DeviceStateManager.DeviceStateCallback.class); + ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); - mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController( - mFakeSettings, - mFakeRotationPolicy, - mDeviceStateManager, - mFakeExecutor, - DEFAULT_SETTINGS - ); + mContentResolver = mContext.getContentResolver(); + mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext); + mDeviceStateRotationLockSettingController = + new DeviceStateRotationLockSettingController( + mFakeRotationPolicy, mDeviceStateManager, mFakeExecutor, mSettingsManager); mDeviceStateRotationLockSettingController.setListening(true); - verify(mDeviceStateManager).registerCallback(any(), - deviceStateCallbackArgumentCaptor.capture()); + verify(mDeviceStateManager) + .registerCallback(any(), deviceStateCallbackArgumentCaptor.capture()); mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue(); } @Test public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() { - mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "", - UserHandle.USER_CURRENT); + initializeSettingsWith(); - mDeviceStateRotationLockSettingController.initialize(); - - assertThat(mFakeSettings - .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - UserHandle.USER_CURRENT)) + assertThat( + Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + UserHandle.USER_CURRENT)) .isEqualTo("0:0:1:2"); } @Test public void whenNoSavedValueForDeviceState_assumeIgnored() { - mFakeSettings.putStringForUser( - Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"0:2:1:2", - UserHandle.USER_CURRENT); + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateRotationLockSettingController.initialize(); mDeviceStateCallback.onStateChanged(1); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); @@ -116,52 +113,43 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase @Test public void whenDeviceStateSwitched_loadCorrectSetting() { - mFakeSettings.putStringForUser( - Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"0:2:1:1", - UserHandle.USER_CURRENT); + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateRotationLockSettingController.initialize(); mDeviceStateCallback.onStateChanged(0); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); mDeviceStateCallback.onStateChanged(1); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); - } @Test public void whenUserChangesSetting_saveSettingForCurrentState() { - mFakeSettings.putStringForUser( - Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"0:1:1:2", - UserHandle.USER_CURRENT); + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); + mSettingsManager.onPersistedSettingsChanged(); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateRotationLockSettingController.initialize(); mDeviceStateCallback.onStateChanged(0); assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); - mDeviceStateRotationLockSettingController - .onRotationLockStateChanged(/* rotationLocked= */false, - /* affordanceVisible= */ true); + mDeviceStateRotationLockSettingController.onRotationLockStateChanged( + /* rotationLocked= */ false, /* affordanceVisible= */ true); - assertThat(mFakeSettings - .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - UserHandle.USER_CURRENT)) + assertThat( + Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + UserHandle.USER_CURRENT)) .isEqualTo("0:2:1:2"); } - @Test public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() { - mFakeSettings.putStringForUser( - Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"0:0:1:2", - UserHandle.USER_CURRENT); + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateRotationLockSettingController.initialize(); mDeviceStateCallback.onStateChanged(1); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); @@ -172,12 +160,9 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase @Test public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() { - mFakeSettings.putStringForUser( - Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - /* value= */"0:0:1:2", - UserHandle.USER_CURRENT); + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); mFakeRotationPolicy.setRotationLock(true); - mDeviceStateRotationLockSettingController.initialize(); mDeviceStateCallback.onStateChanged(1); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); @@ -185,16 +170,52 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase mDeviceStateCallback.onStateChanged(0); assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); - mDeviceStateRotationLockSettingController - .onRotationLockStateChanged(/* rotationLocked= */true, - /* affordanceVisible= */ true); + mDeviceStateRotationLockSettingController.onRotationLockStateChanged( + /* rotationLocked= */ true, /* affordanceVisible= */ true); - assertThat(mFakeSettings - .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, - UserHandle.USER_CURRENT)) + assertThat( + Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + UserHandle.USER_CURRENT)) .isEqualTo("0:0:1:1"); } + @Test + public void whenSettingsChangedExternally_updateRotationPolicy() throws InterruptedException { + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, + 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); + mFakeRotationPolicy.setRotationLock(false); + mDeviceStateCallback.onStateChanged(0); + + assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse(); + + // Changing device state 0 to LOCKED + initializeSettingsWith( + 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED); + + assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue(); + } + + 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(":")) { + sb.append(values[i++]).append(":").append(values[i++]); + } + + Settings.Secure.putStringForUser( + mContentResolver, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK, + sb.toString(), + UserHandle.USER_CURRENT); + + mSettingsManager.onPersistedSettingsChanged(); + } + private static class FakeRotationPolicy implements RotationPolicyWrapper { private boolean mRotationLock; @@ -230,8 +251,8 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase } @Override - public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener, - int userHandle) { + public void registerRotationPolicyListener( + RotationPolicy.RotationPolicyListener listener, int userHandle) { throw new AssertionError("Not implemented"); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java index 0581264d18e2..ea620a6856f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java @@ -23,7 +23,6 @@ import static org.mockito.Mockito.verifyZeroInteractions; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.testing.TestableResources; import androidx.test.filters.SmallTest; @@ -43,25 +42,19 @@ import org.mockito.MockitoAnnotations; @SmallTest public class RotationLockControllerImplTest extends SysuiTestCase { - private static final String[] DEFAULT_SETTINGS = new String[]{ - "0:0", - "1:2" - }; + private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"}; @Mock RotationPolicyWrapper mRotationPolicyWrapper; @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; - private TestableResources mResources; - private ArgumentCaptor - mRotationPolicyListenerCaptor; + private ArgumentCaptor mRotationPolicyListenerCaptor; @Before public void setUp() { MockitoAnnotations.initMocks(/* testClass= */ this); - mResources = mContext.getOrCreateTestableResources(); - mRotationPolicyListenerCaptor = ArgumentCaptor.forClass( - RotationPolicy.RotationPolicyListener.class); + mRotationPolicyListenerCaptor = + ArgumentCaptor.forClass(RotationPolicy.RotationPolicyListener.class); } @Test @@ -79,14 +72,7 @@ public class RotationLockControllerImplTest extends SysuiTestCase { } @Test - public void whenFlagOn_initializesDeviceStateRotationController() { - createRotationLockController(); - - verify(mDeviceStateRotationLockSettingController).initialize(); - } - - @Test - public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() { + public void whenFlagOn_deviceStateRotationControllerAddedToCallbacks() { createRotationLockController(); captureRotationPolicyListener().onChange(); @@ -103,11 +89,11 @@ public class RotationLockControllerImplTest extends SysuiTestCase { private void createRotationLockController() { createRotationLockController(DEFAULT_SETTINGS); } + private void createRotationLockController(String[] deviceStateRotationLockDefaults) { new RotationLockControllerImpl( mRotationPolicyWrapper, mDeviceStateRotationLockSettingController, - deviceStateRotationLockDefaults - ); + deviceStateRotationLockDefaults); } }