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.tv.settings.privacy; 18 19 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.hardware.SensorPrivacyManager; 24 import android.os.Bundle; 25 import android.util.Log; 26 27 import androidx.annotation.Keep; 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceCategory; 30 import androidx.preference.PreferenceScreen; 31 import androidx.preference.SwitchPreference; 32 33 import com.android.tv.settings.R; 34 import com.android.tv.settings.SettingsPreferenceFragment; 35 import com.android.tv.settings.device.apps.AppManagementFragment; 36 import com.android.tv.settings.overlay.FlavorUtils; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 41 /** 42 * The microphone/camera settings screen in TV settings. 43 * Allows the user to turn of the respective sensor. 44 */ 45 @Keep 46 public class SensorFragment extends SettingsPreferenceFragment { 47 48 private static final String TAG = "SensorFragment"; 49 private static final boolean DEBUG = true; 50 51 public static final String TOGGLE_EXTRA = "toggle"; 52 /** How many recent apps should be shown when the list is collapsed. */ 53 private static final int MAX_RECENT_APPS_COLLAPSED = 2; 54 private List<Preference> mAllRecentAppPrefs; 55 56 private static final String SENSOR_TOGGLE_KEY = "sensor_toggle"; 57 private PrivacyToggle mToggle; 58 private SwitchPreference mSensorToggle; 59 60 private SensorPrivacyManager mSensorPrivacyManager; 61 private final SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener = 62 (sensor, enabled) -> { 63 if (mSensorToggle != null) { 64 mSensorToggle.setChecked(!enabled); 65 } 66 }; 67 68 @Override onCreate(Bundle savedInstanceState)69 public void onCreate(Bundle savedInstanceState) { 70 mSensorPrivacyManager = (SensorPrivacyManager) 71 getContext().getSystemService(Context.SENSOR_PRIVACY_SERVICE); 72 73 mToggle = (PrivacyToggle) getArguments().get(TOGGLE_EXTRA); 74 if (mToggle == null) { 75 throw new IllegalArgumentException("PrivacyToggle extra missing"); 76 } 77 78 // Calling super at the end, otherwise mSensorPrivacyManager and mToggle are not initialized 79 // during onCreatePreferences. 80 super.onCreate(savedInstanceState); 81 } 82 83 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)84 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 85 Context themedContext = getPreferenceManager().getContext(); 86 PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(themedContext); 87 88 screen.setTitle(mToggle.screenTitle); 89 90 addSensorToggleWithInfo(screen, themedContext); 91 addRecentAppsGroup(screen, themedContext); 92 addPermissionControllerPreference(screen, themedContext); 93 94 setPreferenceScreen(screen); 95 } 96 97 /** 98 * Adds the sensor toggle with an InfoFragment (in two-panel mode) or an info text below (in 99 * one-panel mode). 100 */ addSensorToggleWithInfo(PreferenceScreen screen, Context themedContext)101 private void addSensorToggleWithInfo(PreferenceScreen screen, Context themedContext) { 102 mSensorToggle = new SwitchPreference(themedContext); 103 screen.addPreference(mSensorToggle); 104 mSensorToggle.setKey(SENSOR_TOGGLE_KEY); 105 mSensorToggle.setTitle(mToggle.toggleTitle); 106 mSensorToggle.setSummary(R.string.sensor_toggle_description); 107 mSensorToggle.setFragment(SensorToggleInfoFragment.class.getName()); 108 mSensorToggle.getExtras().putObject(SensorToggleInfoFragment.TOGGLE_EXTRA, mToggle); 109 110 // If privacy is enabled, the sensor access is turned off 111 mSensorToggle.setChecked( 112 !mSensorPrivacyManager.isSensorPrivacyEnabled(mToggle.sensor)); 113 mSensorPrivacyManager.addSensorPrivacyListener(mToggle.sensor, 114 mPrivacyChangedListener); 115 116 if (!FlavorUtils.isTwoPanel(themedContext)) { 117 // Show the toggle info text beneath instead. 118 Preference toggleInfo = new Preference(themedContext); 119 toggleInfo.setLayoutResource(R.xml.sensor_toggle_info); 120 toggleInfo.setSummary(mToggle.toggleInfoText); 121 toggleInfo.setSelectable(false); 122 screen.addPreference(toggleInfo); 123 } 124 } 125 126 /** 127 * Adds section that shows an expandable list of apps that have recently accessed the sensor. 128 */ addRecentAppsGroup(PreferenceScreen screen, Context themedContext)129 private void addRecentAppsGroup(PreferenceScreen screen, Context themedContext) { 130 // Create the Recently Accessed By section. 131 PreferenceCategory recentRequests = new PreferenceCategory(themedContext); 132 recentRequests.setTitle(R.string.recently_accessed_by_category); 133 screen.addPreference(recentRequests); 134 135 // Get recent accesses. 136 List<RecentlyAccessedByUtils.App> recentApps = RecentlyAccessedByUtils.getAppList( 137 themedContext, mToggle.appOps); 138 if (DEBUG) Log.v(TAG, "recently accessed by " + recentApps.size() + " apps"); 139 140 // Create a preference for each access. 141 mAllRecentAppPrefs = new ArrayList<>(recentApps.size()); 142 for (RecentlyAccessedByUtils.App app : recentApps) { 143 if (DEBUG) Log.v(TAG, "last access: " + app.mLastAccess); 144 Preference pref = new Preference(themedContext); 145 pref.setTitle(app.mLabel); 146 pref.setIcon(app.mIcon); 147 pref.setFragment(AppManagementFragment.class.getName()); 148 AppManagementFragment.prepareArgs(pref.getExtras(), app.mPackageName); 149 mAllRecentAppPrefs.add(pref); 150 } 151 152 for (int i = 0; i < MAX_RECENT_APPS_COLLAPSED; i++) { 153 if (mAllRecentAppPrefs.size() > i) { 154 recentRequests.addPreference(mAllRecentAppPrefs.get(i)); 155 } 156 } 157 if (mAllRecentAppPrefs.size() > MAX_RECENT_APPS_COLLAPSED) { 158 Preference showAllRecent = new Preference(themedContext); 159 showAllRecent.setTitle(R.string.recently_accessed_show_all); 160 showAllRecent.setOnPreferenceClickListener(preference -> { 161 preference.setVisible(false); 162 for (int i = MAX_RECENT_APPS_COLLAPSED; i < mAllRecentAppPrefs.size(); i++) { 163 recentRequests.addPreference(mAllRecentAppPrefs.get(i)); 164 } 165 return false; 166 }); 167 recentRequests.addPreference(showAllRecent); 168 } 169 170 if (mAllRecentAppPrefs.size() == 0) { 171 Preference banner = new Preference(themedContext); 172 banner.setSummary(R.string.no_recent_sensor_accesses); 173 banner.setSelectable(false); 174 recentRequests.addPreference(banner); 175 } 176 } 177 178 /** 179 * Adds a preference that opens the overview of the PermissionGroup pertaining to the sensor. 180 */ addPermissionControllerPreference(PreferenceScreen screen, Context themedContext)181 private void addPermissionControllerPreference(PreferenceScreen screen, Context themedContext) { 182 Preference openPermissionController = new Preference(themedContext); 183 openPermissionController.setTitle(mToggle.appPermissionsTitle); 184 Intent showSensorPermissions = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS); 185 showSensorPermissions.putExtra(Intent.EXTRA_PERMISSION_NAME, 186 mToggle.permissionsGroupName); 187 openPermissionController.setIntent(showSensorPermissions); 188 screen.addPreference(openPermissionController); 189 } 190 191 @Override onDestroy()192 public void onDestroy() { 193 mSensorPrivacyManager.removeSensorPrivacyListener(mToggle.sensor, mPrivacyChangedListener); 194 super.onDestroy(); 195 } 196 197 @Override onPreferenceTreeClick(Preference preference)198 public boolean onPreferenceTreeClick(Preference preference) { 199 if (SENSOR_TOGGLE_KEY.equals(preference.getKey())) { 200 mSensorPrivacyManager.setSensorPrivacy(SETTINGS, mToggle.sensor, 201 !mSensorToggle.isChecked()); 202 return true; 203 } 204 return super.onPreferenceTreeClick(preference); 205 } 206 } 207