1 /* 2 * Copyright (C) 2017 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.accessibility; 18 19 import static android.content.Context.ACCESSIBILITY_SERVICE; 20 21 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 22 23 import android.accessibilityservice.AccessibilityServiceInfo; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.tvsettings.TvSettingsEnums; 26 import android.content.ComponentName; 27 import android.content.pm.ServiceInfo; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.provider.Settings; 31 import android.text.TextUtils; 32 import android.view.accessibility.AccessibilityManager; 33 34 import androidx.annotation.Keep; 35 import androidx.preference.Preference; 36 import androidx.preference.PreferenceGroup; 37 import androidx.preference.SwitchPreference; 38 import androidx.preference.TwoStatePreference; 39 40 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 41 import com.android.settingslib.RestrictedLockUtilsInternal; 42 import com.android.settingslib.RestrictedPreference; 43 import com.android.settingslib.accessibility.AccessibilityUtils; 44 import com.android.tv.settings.R; 45 import com.android.tv.settings.SettingsPreferenceFragment; 46 47 import java.util.List; 48 import java.util.Set; 49 50 /** 51 * Fragment for Accessibility settings 52 */ 53 @Keep 54 public class AccessibilityFragment extends SettingsPreferenceFragment { 55 private static final String TOGGLE_HIGH_TEXT_CONTRAST_KEY = "toggle_high_text_contrast"; 56 private static final String TOGGLE_AUDIO_DESCRIPTION_KEY = "toggle_audio_description"; 57 private static final String ACCESSIBILITY_SERVICES_KEY = "system_accessibility_services"; 58 59 private PreferenceGroup mServicesPref; 60 private AccessibilityManager.AccessibilityStateChangeListener 61 mAccessibilityStateChangeListener = enabled -> refreshServices(mServicesPref); 62 63 /** 64 * Create a new instance of the fragment 65 * @return New fragment instance 66 */ newInstance()67 public static AccessibilityFragment newInstance() { 68 return new AccessibilityFragment(); 69 } 70 71 @Override onResume()72 public void onResume() { 73 super.onResume(); 74 if (mServicesPref != null) { 75 refreshServices(mServicesPref); 76 } 77 } 78 79 @Override onStop()80 public void onStop() { 81 super.onStop(); 82 AccessibilityManager am = (AccessibilityManager) 83 getContext().getSystemService(ACCESSIBILITY_SERVICE); 84 if (am != null && mServicesPref != null) { 85 am.removeAccessibilityStateChangeListener(mAccessibilityStateChangeListener); 86 } 87 } 88 89 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)90 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 91 setPreferencesFromResource(R.xml.accessibility, null); 92 93 final TwoStatePreference highContrastPreference = 94 (TwoStatePreference) findPreference(TOGGLE_HIGH_TEXT_CONTRAST_KEY); 95 highContrastPreference.setChecked(Settings.Secure.getInt(getContext().getContentResolver(), 96 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1); 97 98 final TwoStatePreference audioDescriptionPreference = 99 (TwoStatePreference) findPreference(TOGGLE_AUDIO_DESCRIPTION_KEY); 100 audioDescriptionPreference.setChecked(Settings.Secure.getInt( 101 getContext().getContentResolver(), 102 Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, 0) == 1); 103 104 mServicesPref = (PreferenceGroup) findPreference(ACCESSIBILITY_SERVICES_KEY); 105 if (mServicesPref != null) { 106 refreshServices(mServicesPref); 107 AccessibilityManager am = (AccessibilityManager) 108 getContext().getSystemService(ACCESSIBILITY_SERVICE); 109 if (am != null) { 110 am.addAccessibilityStateChangeListener(mAccessibilityStateChangeListener); 111 } 112 } 113 } 114 115 @Override onPreferenceTreeClick(Preference preference)116 public boolean onPreferenceTreeClick(Preference preference) { 117 if (TextUtils.equals(preference.getKey(), TOGGLE_HIGH_TEXT_CONTRAST_KEY)) { 118 logToggleInteracted( 119 TvSettingsEnums.SYSTEM_A11Y_HIGH_CONTRAST_TEXT, 120 ((SwitchPreference) preference).isChecked()); 121 Settings.Secure.putInt(getActivity().getContentResolver(), 122 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 123 (((SwitchPreference) preference).isChecked() ? 1 : 0)); 124 return true; 125 } else if (TextUtils.equals(preference.getKey(), TOGGLE_AUDIO_DESCRIPTION_KEY)) { 126 logToggleInteracted( 127 TvSettingsEnums.SYSTEM_A11Y_AUDIO_DESCRIPTION, 128 ((SwitchPreference) preference).isChecked()); 129 Settings.Secure.putInt(getActivity().getContentResolver(), 130 Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, 131 (((SwitchPreference) preference).isChecked() ? 1 : 0)); 132 return true; 133 } else { 134 return super.onPreferenceTreeClick(preference); 135 } 136 } 137 refreshServices(PreferenceGroup group)138 private void refreshServices(PreferenceGroup group) { 139 DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class); 140 final List<AccessibilityServiceInfo> installedServiceInfos = 141 getActivity().getSystemService(AccessibilityManager.class) 142 .getInstalledAccessibilityServiceList(); 143 final Set<ComponentName> enabledServices = 144 AccessibilityUtils.getEnabledServicesFromSettings(getActivity()); 145 final List<String> permittedServices = dpm.getPermittedAccessibilityServices( 146 UserHandle.myUserId()); 147 148 final boolean accessibilityEnabled = Settings.Secure.getInt( 149 getActivity().getContentResolver(), 150 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 151 152 for (final AccessibilityServiceInfo accInfo : installedServiceInfos) { 153 final ServiceInfo serviceInfo = accInfo.getResolveInfo().serviceInfo; 154 final ComponentName componentName = new ComponentName(serviceInfo.packageName, 155 serviceInfo.name); 156 157 final boolean serviceEnabled = accessibilityEnabled 158 && enabledServices.contains(componentName); 159 // permittedServices null means all accessibility services are allowed. 160 final boolean serviceAllowed = permittedServices == null 161 || permittedServices.contains(serviceInfo.packageName); 162 163 final String title = accInfo.getResolveInfo() 164 .loadLabel(getActivity().getPackageManager()).toString(); 165 166 final String key = "ServicePref:" + componentName.flattenToString(); 167 RestrictedPreference servicePref = findPreference(key); 168 if (servicePref == null) { 169 servicePref = new RestrictedPreference(group.getContext()); 170 servicePref.setKey(key); 171 } 172 servicePref.setTitle(title); 173 servicePref.setSummary(serviceEnabled ? R.string.settings_on : R.string.settings_off); 174 AccessibilityServiceFragment.prepareArgs(servicePref.getExtras(), 175 serviceInfo.packageName, 176 serviceInfo.name, 177 accInfo.getSettingsActivityName(), 178 title); 179 180 if (serviceAllowed || serviceEnabled) { 181 servicePref.setEnabled(true); 182 servicePref.setFragment(AccessibilityServiceFragment.class.getName()); 183 } else { 184 // Disable accessibility service that are not permitted. 185 final EnforcedAdmin admin = 186 RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed( 187 getContext(), serviceInfo.packageName, UserHandle.myUserId()); 188 if (admin != null) { 189 servicePref.setDisabledByAdmin(admin); 190 } else { 191 servicePref.setEnabled(false); 192 } 193 servicePref.setFragment(null); 194 } 195 196 group.addPreference(servicePref); 197 } 198 } 199 200 @Override getPageId()201 protected int getPageId() { 202 return TvSettingsEnums.SYSTEM_A11Y; 203 } 204 } 205