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 com.android.settings.display.SmartAutoRotateController.hasSufficientPermission;
23 import static com.android.settings.display.SmartAutoRotateController.isRotationResolverServiceAvailable;
24 
25 import android.app.settings.SettingsEnums;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.hardware.SensorPrivacyManager;
31 import android.os.PowerManager;
32 import android.os.UserHandle;
33 import android.provider.Settings;
34 import android.text.TextUtils;
35 
36 import androidx.preference.Preference;
37 import androidx.preference.PreferenceScreen;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.view.RotationPolicy;
41 import com.android.settings.R;
42 import com.android.settings.core.TogglePreferenceController;
43 import com.android.settings.overlay.FeatureFactory;
44 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
45 import com.android.settingslib.core.lifecycle.LifecycleObserver;
46 import com.android.settingslib.core.lifecycle.events.OnStart;
47 import com.android.settingslib.core.lifecycle.events.OnStop;
48 
49 /**
50  * SmartAutoRotatePreferenceController provides auto rotate summary in display settings
51  */
52 public class SmartAutoRotatePreferenceController extends TogglePreferenceController
53         implements LifecycleObserver, OnStart, OnStop {
54 
55     private final MetricsFeatureProvider mMetricsFeatureProvider;
56     private final SensorPrivacyManager mPrivacyManager;
57     private final PowerManager mPowerManager;
58     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
59         @Override
60         public void onReceive(Context context, Intent intent) {
61             refreshSummary(mPreference);
62         }
63     };
64 
65     private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
66     private Preference mPreference;
67 
SmartAutoRotatePreferenceController(Context context, String preferenceKey)68     public SmartAutoRotatePreferenceController(Context context, String preferenceKey) {
69         super(context, preferenceKey);
70         mPrivacyManager = SensorPrivacyManager.getInstance(context);
71         mPrivacyManager
72                 .addSensorPrivacyListener(CAMERA, (sensor, enabled) -> refreshSummary(mPreference));
73         mPowerManager = context.getSystemService(PowerManager.class);
74         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
75     }
76 
77     @Override
getAvailabilityStatus()78     public int getAvailabilityStatus() {
79         return RotationPolicy.isRotationLockToggleVisible(mContext)
80                 ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
81     }
82 
83     @Override
isSliceable()84     public boolean isSliceable() {
85         return TextUtils.equals(getPreferenceKey(), "auto_rotate");
86     }
87 
88     @Override
isPublicSlice()89     public boolean isPublicSlice() {
90         return true;
91     }
92 
93     @Override
getSliceHighlightMenuRes()94     public int getSliceHighlightMenuRes() {
95         return R.string.menu_key_display;
96     }
97 
98     @Override
displayPreference(PreferenceScreen screen)99     public void displayPreference(PreferenceScreen screen) {
100         super.displayPreference(screen);
101         mPreference = screen.findPreference(getPreferenceKey());
102     }
103 
104     @Override
updateState(Preference preference)105     public void updateState(Preference preference) {
106         super.updateState(preference);
107         refreshSummary(mPreference);
108     }
109 
110     @Override
onStart()111     public void onStart() {
112         mContext.registerReceiver(mReceiver,
113                 new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
114         if (mRotationPolicyListener == null) {
115             mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
116                 @Override
117                 public void onChange() {
118                     if (mPreference != null) {
119                         updateState(mPreference);
120                     }
121                 }
122             };
123         }
124         RotationPolicy.registerRotationPolicyListener(mContext,
125                 mRotationPolicyListener);
126     }
127 
128     @Override
onStop()129     public void onStop() {
130         mContext.unregisterReceiver(mReceiver);
131         if (mRotationPolicyListener != null) {
132             RotationPolicy.unregisterRotationPolicyListener(mContext,
133                     mRotationPolicyListener);
134         }
135     }
136 
137     /**
138      * Need this because all controller tests use Roboelectric. No easy way to mock this service,
139      * so we mock the call we need
140      */
141     @VisibleForTesting
isCameraLocked()142     boolean isCameraLocked() {
143         return mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA);
144     }
145 
146     @VisibleForTesting
isPowerSaveMode()147     boolean isPowerSaveMode() {
148         return mPowerManager.isPowerSaveMode();
149     }
150 
151     @Override
isChecked()152     public boolean isChecked() {
153         return !RotationPolicy.isRotationLocked(mContext);
154     }
155 
156     @Override
setChecked(boolean isChecked)157     public boolean setChecked(boolean isChecked) {
158         final boolean isLocked = !isChecked;
159         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATION_LOCK,
160                 isLocked);
161         RotationPolicy.setRotationLock(mContext, isLocked);
162         return true;
163     }
164 
165     @Override
getSummary()166     public CharSequence getSummary() {
167         int activeStringId = R.string.auto_rotate_option_off;
168         if (!RotationPolicy.isRotationLocked(mContext)) {
169             final int cameraRotate = Settings.Secure.getIntForUser(
170                     mContext.getContentResolver(),
171                     CAMERA_AUTOROTATE,
172                     0, UserHandle.USER_CURRENT);
173             activeStringId = cameraRotate == 1 && isRotationResolverServiceAvailable(mContext)
174                     && hasSufficientPermission(mContext)
175                     && !isCameraLocked()
176                     && !isPowerSaveMode()
177                     ? R.string.auto_rotate_option_face_based
178                     : R.string.auto_rotate_option_on;
179         }
180         return mContext.getString(activeStringId);
181     }
182 }
183