1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.display; 18 19 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; 20 import static android.provider.Settings.Secure.CAMERA_AUTOROTATE; 21 22 import static androidx.lifecycle.Lifecycle.Event.ON_START; 23 import static androidx.lifecycle.Lifecycle.Event.ON_STOP; 24 25 import static com.android.settings.display.SmartAutoRotateController.hasSufficientPermission; 26 import static com.android.settings.display.SmartAutoRotateController.isRotationResolverServiceAvailable; 27 28 import android.app.settings.SettingsEnums; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.hardware.SensorPrivacyManager; 34 import android.os.PowerManager; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.text.TextUtils; 38 39 import androidx.lifecycle.LifecycleObserver; 40 import androidx.lifecycle.OnLifecycleEvent; 41 import androidx.preference.Preference; 42 import androidx.preference.PreferenceScreen; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.view.RotationPolicy; 46 import com.android.settings.R; 47 import com.android.settings.core.TogglePreferenceController; 48 import com.android.settings.overlay.FeatureFactory; 49 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 50 51 /** 52 * SmartAutoRotatePreferenceController provides auto rotate summary in display settings 53 */ 54 public class SmartAutoRotatePreferenceController extends TogglePreferenceController 55 implements LifecycleObserver { 56 57 private final MetricsFeatureProvider mMetricsFeatureProvider; 58 private final SensorPrivacyManager mPrivacyManager; 59 private final PowerManager mPowerManager; 60 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 61 @Override 62 public void onReceive(Context context, Intent intent) { 63 refreshSummary(mPreference); 64 } 65 }; 66 67 private final SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener = 68 new SensorPrivacyManager.OnSensorPrivacyChangedListener() { 69 @Override 70 public void onSensorPrivacyChanged(int sensor, boolean enabled) { 71 refreshSummary(mPreference); 72 } 73 }; 74 75 private RotationPolicy.RotationPolicyListener mRotationPolicyListener; 76 private Preference mPreference; 77 SmartAutoRotatePreferenceController(Context context, String preferenceKey)78 public SmartAutoRotatePreferenceController(Context context, String preferenceKey) { 79 super(context, preferenceKey); 80 mPrivacyManager = SensorPrivacyManager.getInstance(context); 81 mPowerManager = context.getSystemService(PowerManager.class); 82 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 83 } 84 85 @Override getAvailabilityStatus()86 public int getAvailabilityStatus() { 87 return RotationPolicy.isRotationLockToggleVisible(mContext) 88 && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext) 89 ? AVAILABLE : UNSUPPORTED_ON_DEVICE; 90 } 91 92 @Override isSliceable()93 public boolean isSliceable() { 94 return TextUtils.equals(getPreferenceKey(), "auto_rotate"); 95 } 96 97 @Override isPublicSlice()98 public boolean isPublicSlice() { 99 return true; 100 } 101 102 @Override getSliceHighlightMenuRes()103 public int getSliceHighlightMenuRes() { 104 return R.string.menu_key_display; 105 } 106 107 @Override displayPreference(PreferenceScreen screen)108 public void displayPreference(PreferenceScreen screen) { 109 super.displayPreference(screen); 110 mPreference = screen.findPreference(getPreferenceKey()); 111 } 112 113 @Override updateState(Preference preference)114 public void updateState(Preference preference) { 115 super.updateState(preference); 116 refreshSummary(mPreference); 117 } 118 119 @OnLifecycleEvent(ON_START) onStart()120 public void onStart() { 121 mContext.registerReceiver(mReceiver, 122 new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); 123 if (mRotationPolicyListener == null) { 124 mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() { 125 @Override 126 public void onChange() { 127 if (mPreference != null) { 128 updateState(mPreference); 129 } 130 } 131 }; 132 } 133 RotationPolicy.registerRotationPolicyListener(mContext, 134 mRotationPolicyListener); 135 mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener); 136 } 137 138 @OnLifecycleEvent(ON_STOP) onStop()139 public void onStop() { 140 mContext.unregisterReceiver(mReceiver); 141 if (mRotationPolicyListener != null) { 142 RotationPolicy.unregisterRotationPolicyListener(mContext, 143 mRotationPolicyListener); 144 } 145 mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener); 146 } 147 148 /** 149 * Need this because all controller tests use Roboelectric. No easy way to mock this service, 150 * so we mock the call we need 151 */ 152 @VisibleForTesting isCameraLocked()153 boolean isCameraLocked() { 154 return mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA); 155 } 156 157 @VisibleForTesting isPowerSaveMode()158 boolean isPowerSaveMode() { 159 return mPowerManager.isPowerSaveMode(); 160 } 161 162 @Override isChecked()163 public boolean isChecked() { 164 return !RotationPolicy.isRotationLocked(mContext); 165 } 166 167 @Override setChecked(boolean isChecked)168 public boolean setChecked(boolean isChecked) { 169 final boolean isLocked = !isChecked; 170 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATION_LOCK, 171 isLocked); 172 RotationPolicy.setRotationLock(mContext, isLocked, 173 /* caller= */ "SmartAutoRotatePreferenceController#setChecked"); 174 return true; 175 } 176 177 @Override getSummary()178 public CharSequence getSummary() { 179 int activeStringId = R.string.auto_rotate_option_off; 180 if (!RotationPolicy.isRotationLocked(mContext)) { 181 final int cameraRotate = Settings.Secure.getIntForUser( 182 mContext.getContentResolver(), 183 CAMERA_AUTOROTATE, 184 0, UserHandle.USER_CURRENT); 185 activeStringId = cameraRotate == 1 && isRotationResolverServiceAvailable(mContext) 186 && hasSufficientPermission(mContext) 187 && !isCameraLocked() 188 && !isPowerSaveMode() 189 ? R.string.auto_rotate_option_face_based 190 : R.string.auto_rotate_option_on; 191 } 192 return mContext.getString(activeStringId); 193 } 194 }