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 com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 20 21 import android.app.admin.DevicePolicyManager; 22 import android.app.tvsettings.TvSettingsEnums; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.text.TextUtils; 29 30 import androidx.annotation.NonNull; 31 import androidx.preference.Preference; 32 import androidx.preference.PreferenceScreen; 33 34 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 35 import com.android.settingslib.RestrictedLockUtilsInternal; 36 import com.android.settingslib.RestrictedSwitchPreference; 37 import com.android.settingslib.accessibility.AccessibilityUtils; 38 import com.android.tv.settings.R; 39 import com.android.tv.settings.SettingsPreferenceFragment; 40 41 import java.util.List; 42 import java.util.Set; 43 44 /** 45 * Fragment for controlling accessibility service 46 */ 47 public class AccessibilityServiceFragment extends SettingsPreferenceFragment implements 48 AccessibilityServiceConfirmationFragment.OnAccessibilityServiceConfirmedListener { 49 private static final String ARG_PACKAGE_NAME = "packageName"; 50 private static final String ARG_SERVICE_NAME = "serviceName"; 51 private static final String ARG_SETTINGS_ACTIVITY_NAME = "settingsActivityName"; 52 private static final String ARG_LABEL = "label"; 53 54 private RestrictedSwitchPreference mEnablePref; 55 56 /** 57 * Put args in bundle 58 * @param args Bundle to prepare 59 * @param packageName Package of accessibility service 60 * @param serviceName Class of accessibility service 61 * @param activityName Class of accessibility service settings activity 62 * @param label Screen title 63 */ prepareArgs(@onNull Bundle args, String packageName, String serviceName, String activityName, String label)64 public static void prepareArgs(@NonNull Bundle args, String packageName, String serviceName, 65 String activityName, String label) { 66 args.putString(ARG_PACKAGE_NAME, packageName); 67 args.putString(ARG_SERVICE_NAME, serviceName); 68 args.putString(ARG_SETTINGS_ACTIVITY_NAME, activityName); 69 args.putString(ARG_LABEL, label); 70 } 71 72 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)73 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 74 final Context themedContext = getPreferenceManager().getContext(); 75 final PreferenceScreen screen = 76 getPreferenceManager().createPreferenceScreen(themedContext); 77 screen.setTitle(getArguments().getString(ARG_LABEL)); 78 79 mEnablePref = new RestrictedSwitchPreference(themedContext); 80 mEnablePref.setTitle(R.string.system_accessibility_status); 81 mEnablePref.setFragment(AccessibilityServiceConfirmationFragment.class.getName()); 82 screen.addPreference(mEnablePref); 83 84 final Preference settingsPref = new Preference(themedContext); 85 settingsPref.setTitle(R.string.system_accessibility_config); 86 final String activityName = getArguments().getString(ARG_SETTINGS_ACTIVITY_NAME); 87 final String packageName = getArguments().getString(ARG_PACKAGE_NAME); 88 if (!TextUtils.isEmpty(activityName) && !TextUtils.isEmpty(packageName)) { 89 settingsPref.setIntent(new Intent(Intent.ACTION_MAIN) 90 .setComponent(new ComponentName(packageName, activityName))); 91 } else { 92 settingsPref.setEnabled(false); 93 } 94 screen.addPreference(settingsPref); 95 96 final String serviceName = getArguments().getString(ARG_SERVICE_NAME); 97 ComponentName serviceComponent=null; 98 if(!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(serviceName)){ 99 serviceComponent = new ComponentName(packageName, serviceName); 100 } 101 if (serviceComponent != null 102 && serviceComponent 103 .flattenToString() 104 .equals( 105 getResources() 106 .getString(R.string. 107 accessibility_screen_reader_flattened_component_name))) { 108 final Preference screenReaderPref = new Preference(themedContext); 109 screenReaderPref.setTitle( 110 getResources().getString( 111 R.string.screen_reader_service_title)); 112 screenReaderPref.setSummary(R.string.screen_reader_summary); 113 screenReaderPref.setEnabled(false); 114 screen.addPreference(screenReaderPref); 115 } 116 117 setPreferenceScreen(screen); 118 } 119 120 @Override onPreferenceTreeClick(Preference preference)121 public boolean onPreferenceTreeClick(Preference preference) { 122 if (preference == mEnablePref && !mEnablePref.isDisabledByAdmin()) { 123 // Prepare confirmation dialog and reverts switch until result comes back. 124 updateEnablePref(); 125 // Pass to super to launch confirmation dialog. 126 super.onPreferenceTreeClick(preference); 127 return true; 128 } else { 129 return super.onPreferenceTreeClick(preference); 130 } 131 } 132 updateEnablePref()133 private void updateEnablePref() { 134 final String packageName = getArguments().getString(ARG_PACKAGE_NAME); 135 final String serviceName = getArguments().getString(ARG_SERVICE_NAME); 136 final ComponentName serviceComponent = new ComponentName(packageName, serviceName); 137 final Set<ComponentName> enabledServices = 138 AccessibilityUtils.getEnabledServicesFromSettings(getActivity()); 139 final boolean enabled = enabledServices.contains(serviceComponent); 140 mEnablePref.setChecked(enabled); 141 AccessibilityServiceConfirmationFragment.prepareArgs(mEnablePref.getExtras(), 142 new ComponentName(packageName, serviceName), 143 getArguments().getString(ARG_LABEL), !enabled); 144 145 DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class); 146 final List<String> permittedServices = dpm.getPermittedAccessibilityServices( 147 UserHandle.myUserId()); 148 final boolean serviceAllowed = permittedServices == null 149 || permittedServices.contains(packageName); 150 151 if (serviceAllowed || enabled) { 152 mEnablePref.setEnabled(true); 153 } else { 154 // Disable accessibility service that are not permitted. 155 final EnforcedAdmin admin = 156 RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed( 157 getContext(), packageName, UserHandle.myUserId()); 158 if (admin != null) { 159 mEnablePref.setDisabledByAdmin(admin); 160 } else { 161 mEnablePref.setEnabled(false); 162 } 163 } 164 } 165 166 @Override onResume()167 public void onResume() { 168 super.onResume(); 169 updateEnablePref(); 170 } 171 172 @Override onAccessibilityServiceConfirmed(ComponentName componentName, boolean enabling)173 public void onAccessibilityServiceConfirmed(ComponentName componentName, boolean enabling) { 174 AccessibilityUtils.setAccessibilityServiceState(getActivity(), componentName, enabling); 175 if (mEnablePref != null) { 176 mEnablePref.setChecked(enabling); 177 // Check if component is talkback, then log 178 if (componentName != null 179 && componentName.flattenToString().equals( 180 getResources().getString( 181 R.string.accessibility_screen_reader_flattened_component_name))) { 182 logToggleInteracted(TvSettingsEnums.SYSTEM_A11Y_TALKBACK_ON_OFF, enabling); 183 } 184 } 185 } 186 187 @Override getPageId()188 protected int getPageId() { 189 // Partial matching of Service's Android componentName for logging a11y services. 190 String serviceName = getArguments().getString(ARG_SERVICE_NAME); 191 if (TextUtils.isEmpty(serviceName)) { 192 return TvSettingsEnums.PAGE_CLASSIC_DEFAULT; 193 } else if (serviceName.contains("TalkBack")) { 194 return TvSettingsEnums.SYSTEM_A11Y_TALKBACK; 195 } else if (serviceName.contains("AccessibilityMenu")) { 196 return TvSettingsEnums.SYSTEM_A11Y_A11Y_MENU; 197 } else if (serviceName.contains("SelectToSpeak")) { 198 return TvSettingsEnums.SYSTEM_A11Y_STS; 199 } else if (serviceName.contains("SwitchAccess")) { 200 return TvSettingsEnums.SYSTEM_A11Y_SWITCH_ACCESS; 201 } else { 202 return TvSettingsEnums.PAGE_CLASSIC_DEFAULT; 203 } 204 } 205 } 206