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