| /* |
| * Copyright (C) 2021 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 android.annotation.Nullable; |
| import android.hardware.devicestate.DeviceStateManager; |
| import android.os.Trace; |
| import android.util.IndentingPrintWriter; |
| |
| import androidx.annotation.NonNull; |
| |
| import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; |
| import com.android.systemui.Dumpable; |
| import com.android.systemui.dagger.SysUISingleton; |
| import com.android.systemui.dagger.qualifiers.Main; |
| import com.android.systemui.dump.DumpManager; |
| import com.android.systemui.util.wrapper.RotationPolicyWrapper; |
| |
| import java.io.PrintWriter; |
| import java.util.concurrent.Executor; |
| |
| import javax.inject.Inject; |
| |
| /** |
| * 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, Dumpable { |
| |
| private final RotationPolicyWrapper mRotationPolicyWrapper; |
| private final DeviceStateManager mDeviceStateManager; |
| private final Executor mMainExecutor; |
| private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager; |
| private final DeviceStateRotationLockSettingControllerLogger mLogger; |
| |
| // 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; |
| private DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener |
| mDeviceStateRotationLockSettingsListener; |
| |
| @Inject |
| public DeviceStateRotationLockSettingController( |
| RotationPolicyWrapper rotationPolicyWrapper, |
| DeviceStateManager deviceStateManager, |
| @Main Executor executor, |
| DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager, |
| DeviceStateRotationLockSettingControllerLogger logger, |
| DumpManager dumpManager) { |
| mRotationPolicyWrapper = rotationPolicyWrapper; |
| mDeviceStateManager = deviceStateManager; |
| mMainExecutor = executor; |
| mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager; |
| mLogger = logger; |
| dumpManager.registerDumpable(this); |
| } |
| |
| @Override |
| public void setListening(boolean listening) { |
| mLogger.logListeningChange(listening); |
| if (listening) { |
| // Note that this is called once with the initial state of the device, even if there |
| // is no user action. |
| mDeviceStateCallback = this::updateDeviceState; |
| mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback); |
| mDeviceStateRotationLockSettingsListener = () -> |
| readPersistedSetting("deviceStateRotationLockChange", mDeviceState); |
| mDeviceStateRotationLockSettingsManager.registerListener( |
| mDeviceStateRotationLockSettingsListener); |
| } else { |
| if (mDeviceStateCallback != null) { |
| mDeviceStateManager.unregisterCallback(mDeviceStateCallback); |
| } |
| if (mDeviceStateRotationLockSettingsListener != null) { |
| mDeviceStateRotationLockSettingsManager.unregisterListener( |
| mDeviceStateRotationLockSettingsListener); |
| } |
| } |
| } |
| |
| @Override |
| public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) { |
| int deviceState = mDeviceState; |
| boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager |
| .isRotationLocked(deviceState); |
| mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked); |
| if (deviceState == -1) { |
| return; |
| } |
| if (newRotationLocked == currentRotationLocked) { |
| return; |
| } |
| saveNewRotationLockSetting(newRotationLocked); |
| } |
| |
| private void saveNewRotationLockSetting(boolean isRotationLocked) { |
| int deviceState = mDeviceState; |
| mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState); |
| mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked); |
| } |
| |
| private void updateDeviceState(int state) { |
| mLogger.logUpdateDeviceState(mDeviceState, state); |
| if (Trace.isEnabled()) { |
| Trace.traceBegin( |
| Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]"); |
| } |
| try { |
| if (mDeviceState == state) { |
| return; |
| } |
| |
| readPersistedSetting("updateDeviceState", state); |
| } finally { |
| Trace.endSection(); |
| } |
| } |
| |
| private void readPersistedSetting(String caller, int state) { |
| int rotationLockSetting = |
| mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state); |
| boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED; |
| boolean isLocked = mRotationPolicyWrapper.isRotationLocked(); |
| |
| mLogger.readPersistedSetting(caller, state, rotationLockSetting, shouldBeLocked, isLocked); |
| |
| 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. |
| return; |
| } |
| |
| // Accept the new state |
| mDeviceState = state; |
| |
| // Update the rotation policy, if needed, for this new device state |
| if (shouldBeLocked != isLocked) { |
| mRotationPolicyWrapper.setRotationLock(shouldBeLocked); |
| } |
| } |
| |
| @Override |
| public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) { |
| IndentingPrintWriter pw = new IndentingPrintWriter(printWriter); |
| mDeviceStateRotationLockSettingsManager.dump(pw); |
| pw.println("DeviceStateRotationLockSettingController"); |
| pw.increaseIndent(); |
| pw.println("mDeviceState: " + mDeviceState); |
| pw.decreaseIndent(); |
| } |
| } |