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