• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.settings.display;
17 
18 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
19 import static android.provider.Settings.Secure.CAMERA_AUTOROTATE;
20 
21 import static androidx.lifecycle.Lifecycle.Event.ON_START;
22 import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
23 
24 import android.Manifest;
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.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.hardware.SensorPrivacyManager;
33 import android.os.PowerManager;
34 import android.provider.Settings;
35 import android.service.rotationresolver.RotationResolverService;
36 import android.text.TextUtils;
37 
38 import androidx.lifecycle.Lifecycle;
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.core.TogglePreferenceController;
47 import com.android.settings.overlay.FeatureFactory;
48 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
49 
50 /**
51  * SmartAutoRotateController controls whether auto rotation is enabled
52  */
53 public class SmartAutoRotateController extends TogglePreferenceController implements
54         Preference.OnPreferenceChangeListener, LifecycleObserver {
55 
56     private final MetricsFeatureProvider mMetricsFeatureProvider;
57     private final SensorPrivacyManager mPrivacyManager;
58     private final PowerManager mPowerManager;
59     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
60         @Override
61         public void onReceive(Context context, Intent intent) {
62             updateState(mPreference);
63         }
64     };
65     private Preference mPreference;
66     private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
67 
SmartAutoRotateController(Context context, String preferenceKey)68     public SmartAutoRotateController(Context context, String preferenceKey) {
69         super(context, preferenceKey);
70         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
71         mPrivacyManager = SensorPrivacyManager.getInstance(context);
72         mPrivacyManager
73                 .addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference));
74         mPowerManager = context.getSystemService(PowerManager.class);
75     }
76 
init(Lifecycle lifecycle)77     public void init(Lifecycle lifecycle) {
78         lifecycle.addObserver(this);
79     }
80 
81     @Override
getAvailabilityStatus()82     public int getAvailabilityStatus() {
83         if (!isRotationResolverServiceAvailable(mContext)) {
84             return UNSUPPORTED_ON_DEVICE;
85         }
86         return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
87                 && !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
88     }
89 
90     @Override
updateState(Preference preference)91     public void updateState(Preference preference) {
92         super.updateState(preference);
93         if (preference != null) {
94             preference.setEnabled(getAvailabilityStatus() == AVAILABLE);
95         }
96     }
97 
98     /**
99      * Need this because all controller tests use RoboElectric. No easy way to mock this service,
100      * so we mock the call we need
101      */
102     @VisibleForTesting
isCameraLocked()103     boolean isCameraLocked() {
104         return mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA);
105     }
106 
107     @VisibleForTesting
isPowerSaveMode()108     boolean isPowerSaveMode() {
109         return mPowerManager.isPowerSaveMode();
110     }
111 
112     @OnLifecycleEvent(ON_START)
onStart()113     public void onStart() {
114         mContext.registerReceiver(mReceiver,
115                 new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
116         if (mRotationPolicyListener == null) {
117             mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
118                 @Override
119                 public void onChange() {
120                     updateState(mPreference);
121                 }
122             };
123         }
124         RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener);
125     }
126 
127     @OnLifecycleEvent(ON_STOP)
onStop()128     public void onStop() {
129         mContext.unregisterReceiver(mReceiver);
130         if (mRotationPolicyListener != null) {
131             RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
132             mRotationPolicyListener = null;
133         }
134     }
135 
136     @Override
isChecked()137     public boolean isChecked() {
138         return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
139                 && !isCameraLocked() && !isPowerSaveMode() && Settings.Secure.getInt(
140                 mContext.getContentResolver(),
141                 CAMERA_AUTOROTATE, 0) == 1;
142     }
143 
144     @Override
displayPreference(PreferenceScreen screen)145     public void displayPreference(PreferenceScreen screen) {
146         super.displayPreference(screen);
147         mPreference = screen.findPreference(getPreferenceKey());
148     }
149 
150     @Override
setChecked(boolean isChecked)151     public boolean setChecked(boolean isChecked) {
152         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_CAMERA_ROTATE_TOGGLE,
153                 isChecked);
154         Settings.Secure.putInt(mContext.getContentResolver(),
155                 CAMERA_AUTOROTATE,
156                 isChecked ? 1 : 0);
157         return true;
158     }
159 
isRotationResolverServiceAvailable(Context context)160     static boolean isRotationResolverServiceAvailable(Context context) {
161         final PackageManager packageManager = context.getPackageManager();
162         final String resolvePackage = packageManager.getRotationResolverPackageName();
163         if (TextUtils.isEmpty(resolvePackage)) {
164             return false;
165         }
166         final Intent intent = new Intent(RotationResolverService.SERVICE_INTERFACE).setPackage(
167                 resolvePackage);
168         final ResolveInfo resolveInfo = packageManager.resolveService(intent,
169                 PackageManager.MATCH_SYSTEM_ONLY);
170         return resolveInfo != null && resolveInfo.serviceInfo != null;
171     }
172 
hasSufficientPermission(Context context)173     static boolean hasSufficientPermission(Context context) {
174         final PackageManager packageManager = context.getPackageManager();
175         final String rotationPackage = packageManager.getRotationResolverPackageName();
176         return rotationPackage != null && packageManager.checkPermission(
177                 Manifest.permission.CAMERA, rotationPackage) == PackageManager.PERMISSION_GRANTED;
178     }
179 }
180