1 /* 2 * Copyright (C) 2009 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.settings.accessibility; 18 19 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT; 20 21 import android.accessibilityservice.AccessibilityServiceInfo; 22 import android.accessibilityservice.AccessibilityShortcutInfo; 23 import android.app.settings.SettingsEnums; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.hardware.input.InputManager; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.UserHandle; 30 import android.provider.Settings; 31 import android.text.TextUtils; 32 import android.util.ArrayMap; 33 import android.view.accessibility.AccessibilityManager; 34 35 import androidx.annotation.NonNull; 36 import androidx.annotation.VisibleForTesting; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceCategory; 39 40 import com.android.internal.accessibility.AccessibilityShortcutController; 41 import com.android.internal.accessibility.util.AccessibilityUtils; 42 import com.android.internal.content.PackageMonitor; 43 import com.android.settings.R; 44 import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType; 45 import com.android.settings.accessibility.actionbar.FeedbackMenuController; 46 import com.android.settings.dashboard.DashboardFragment; 47 import com.android.settings.overlay.FeatureFactory; 48 import com.android.settings.search.BaseSearchIndexProvider; 49 import com.android.settingslib.RestrictedPreference; 50 import com.android.settingslib.core.AbstractPreferenceController; 51 import com.android.settingslib.search.SearchIndexable; 52 import com.android.settingslib.search.SearchIndexableRaw; 53 54 import java.util.ArrayList; 55 import java.util.Collection; 56 import java.util.List; 57 import java.util.Map; 58 59 /** Activity with the accessibility settings. */ 60 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) 61 public class AccessibilitySettings extends DashboardFragment implements 62 InputManager.InputDeviceListener { 63 64 private static final String TAG = "AccessibilitySettings"; 65 66 // Preference categories 67 private static final String CATEGORY_SCREEN_READER = "screen_reader_category"; 68 private static final String CATEGORY_CAPTIONS = "captions_category"; 69 private static final String CATEGORY_AUDIO = "audio_category"; 70 private static final String CATEGORY_SPEECH = "speech_category"; 71 private static final String CATEGORY_DISPLAY = "display_category"; 72 @VisibleForTesting 73 static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category"; 74 @VisibleForTesting 75 static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category"; 76 77 private static final String[] CATEGORIES = new String[]{ 78 CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY, 79 CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES 80 }; 81 82 // Extras passed to sub-fragments. 83 static final String EXTRA_PREFERENCE_KEY = "preference_key"; 84 static final String EXTRA_CHECKED = "checked"; 85 static final String EXTRA_TITLE = "title"; 86 static final String EXTRA_RESOLVE_INFO = "resolve_info"; 87 static final String EXTRA_SUMMARY = "summary"; 88 static final String EXTRA_INTRO = "intro"; 89 static final String EXTRA_SETTINGS_TITLE = "settings_title"; 90 static final String EXTRA_COMPONENT_NAME = "component_name"; 91 static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name"; 92 static final String EXTRA_TILE_SERVICE_COMPONENT_NAME = "tile_service_component_name"; 93 static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw"; 94 static final String EXTRA_ANIMATED_IMAGE_RES = "animated_image_res"; 95 static final String EXTRA_HTML_DESCRIPTION = "html_description"; 96 static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool"; 97 static final String EXTRA_METRICS_CATEGORY = "metrics_category"; 98 static final String EXTRA_FEEDBACK_CATEGORY = "feedback_category"; 99 100 // Timeout before we update the services if packages are added/removed 101 // since the AccessibilityManagerService has to do that processing first 102 // to generate the AccessibilityServiceInfo we need for proper 103 // presentation. 104 private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; 105 106 private final Handler mHandler = new Handler(); 107 108 private final Runnable mUpdateRunnable = new Runnable() { 109 @Override 110 public void run() { 111 if (getActivity() != null) { 112 onContentChanged(); 113 } 114 } 115 }; 116 117 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 118 @Override 119 public void onPackageAdded(String packageName, int uid) { 120 sendUpdate(); 121 } 122 123 @Override 124 public void onPackageModified(@NonNull String packageName) { 125 sendUpdate(); 126 } 127 128 @Override 129 public void onPackageAppeared(String packageName, int reason) { 130 sendUpdate(); 131 } 132 133 @Override 134 public void onPackageDisappeared(String packageName, int reason) { 135 sendUpdate(); 136 } 137 138 @Override 139 public void onPackageRemoved(String packageName, int uid) { 140 sendUpdate(); 141 } 142 143 private void sendUpdate() { 144 mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS); 145 } 146 }; 147 148 private AccessibilitySettingsContentObserver mSettingsContentObserver; 149 150 private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap = 151 new ArrayMap<>(); 152 private final List<Preference> mServicePreferences = new ArrayList<>(); 153 private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap = 154 new ArrayMap<>(); 155 156 private boolean mNeedPreferencesUpdate = false; 157 private boolean mIsForeground = true; 158 AccessibilitySettings()159 public AccessibilitySettings() { 160 mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler); 161 } 162 initializeSettingsContentObserver()163 private void initializeSettingsContentObserver() { 164 // Observe changes to anything that the shortcut can toggle, so we can reflect updates 165 final Collection<AccessibilityShortcutController.FrameworkFeatureInfo> features = 166 AccessibilityShortcutController 167 .getFrameworkShortcutFeaturesMap().values(); 168 final List<String> shortcutFeatureKeys = new ArrayList<>(features.size()); 169 for (AccessibilityShortcutController.FrameworkFeatureInfo feature : features) { 170 final String key = feature.getSettingKey(); 171 if (key != null) { 172 shortcutFeatureKeys.add(key); 173 } 174 } 175 176 // Observe changes from accessibility selection menu 177 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); 178 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); 179 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_QS_TARGETS); 180 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS); 181 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS); 182 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS); 183 mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, 184 key -> onContentChanged()); 185 } 186 187 @Override getMetricsCategory()188 public int getMetricsCategory() { 189 return SettingsEnums.ACCESSIBILITY; 190 } 191 192 @Override getHelpResource()193 public int getHelpResource() { 194 return R.string.help_uri_accessibility; 195 } 196 197 @Override onAttach(Context context)198 public void onAttach(Context context) { 199 super.onAttach(context); 200 use(AccessibilityHearingAidPreferenceController.class) 201 .setFragmentManager(getFragmentManager()); 202 } 203 204 @Override onCreate(Bundle icicle)205 public void onCreate(Bundle icicle) { 206 super.onCreate(icicle); 207 initializeSettingsContentObserver(); 208 initializeAllPreferences(); 209 updateAllPreferences(); 210 mNeedPreferencesUpdate = false; 211 registerContentMonitors(); 212 registerInputDeviceListener(); 213 FeedbackMenuController.init(this, SettingsEnums.ACCESSIBILITY); 214 } 215 216 @Override onStart()217 public void onStart() { 218 super.onStart(); 219 mIsForeground = true; 220 } 221 222 @Override onResume()223 public void onResume() { 224 super.onResume(); 225 if (mNeedPreferencesUpdate) { 226 updateAllPreferences(); 227 mNeedPreferencesUpdate = false; 228 } 229 } 230 231 @Override onPause()232 public void onPause() { 233 super.onPause(); 234 mNeedPreferencesUpdate = true; 235 } 236 237 @Override onStop()238 public void onStop() { 239 mIsForeground = false; 240 super.onStop(); 241 } 242 243 @Override onDestroy()244 public void onDestroy() { 245 unregisterContentMonitors(); 246 unRegisterInputDeviceListener(); 247 super.onDestroy(); 248 } 249 250 @Override getPreferenceScreenResId()251 protected int getPreferenceScreenResId() { 252 return R.xml.accessibility_settings; 253 } 254 255 @Override getLogTag()256 protected String getLogTag() { 257 return TAG; 258 } 259 260 /** 261 * Returns the summary for the current state of this accessibilityService. 262 * 263 * @param context A valid context 264 * @param info The accessibilityService's info 265 * @param serviceEnabled Whether the accessibility service is enabled. 266 * @return The service summary 267 */ getServiceSummary(Context context, AccessibilityServiceInfo info, boolean serviceEnabled)268 public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info, 269 boolean serviceEnabled) { 270 if (serviceEnabled && info.crashed) { 271 return context.getText(R.string.accessibility_summary_state_stopped); 272 } 273 274 final CharSequence serviceState; 275 final int fragmentType = AccessibilityUtil.getAccessibilityServiceFragmentType(info); 276 if (fragmentType == AccessibilityServiceFragmentType.INVISIBLE_TOGGLE) { 277 final ComponentName componentName = new ComponentName( 278 info.getResolveInfo().serviceInfo.packageName, 279 info.getResolveInfo().serviceInfo.name); 280 final boolean shortcutEnabled = AccessibilityUtil.getUserShortcutTypesFromSettings( 281 context, componentName) != DEFAULT; 282 serviceState = shortcutEnabled 283 ? context.getText(R.string.accessibility_summary_shortcut_enabled) 284 : context.getText(R.string.generic_accessibility_feature_shortcut_off); 285 } else { 286 serviceState = serviceEnabled 287 ? context.getText(R.string.generic_accessibility_service_on) 288 : context.getText(R.string.generic_accessibility_service_off); 289 } 290 291 final CharSequence serviceSummary = info.loadSummary(context.getPackageManager()); 292 final String stateSummaryCombo = context.getString( 293 com.android.settingslib.R.string.preference_summary_default_combination, 294 serviceState, serviceSummary); 295 296 return TextUtils.isEmpty(serviceSummary) ? serviceState : stateSummaryCombo; 297 } 298 299 /** 300 * Returns the description for the current state of this accessibilityService. 301 * 302 * @param context A valid context 303 * @param info The accessibilityService's info 304 * @param serviceEnabled Whether the accessibility service is enabled. 305 * @return The service description 306 */ getServiceDescription(Context context, AccessibilityServiceInfo info, boolean serviceEnabled)307 public static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info, 308 boolean serviceEnabled) { 309 if (serviceEnabled && info.crashed) { 310 return context.getText(R.string.accessibility_description_state_stopped); 311 } 312 313 return info.loadDescription(context.getPackageManager()); 314 } 315 316 @VisibleForTesting onContentChanged()317 void onContentChanged() { 318 // If the fragment is visible then update preferences immediately, else set the flag then 319 // wait for the fragment to show up to update preferences. 320 if (mIsForeground) { 321 updateAllPreferences(); 322 } else { 323 mNeedPreferencesUpdate = true; 324 } 325 } 326 initializeAllPreferences()327 private void initializeAllPreferences() { 328 for (int i = 0; i < CATEGORIES.length; i++) { 329 PreferenceCategory prefCategory = findPreference(CATEGORIES[i]); 330 mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory); 331 } 332 } 333 334 @VisibleForTesting updateAllPreferences()335 void updateAllPreferences() { 336 updateServicePreferences(); 337 updatePreferencesState(); 338 updateSystemPreferences(); 339 } 340 registerContentMonitors()341 private void registerContentMonitors() { 342 final Context context = getActivity(); 343 344 mSettingsPackageMonitor.register(context, context.getMainLooper(), /* externalStorage= */ 345 false); 346 mSettingsContentObserver.register(getContentResolver()); 347 } 348 registerInputDeviceListener()349 private void registerInputDeviceListener() { 350 InputManager mIm = getSystemService(InputManager.class); 351 if (mIm == null) { 352 return; 353 } 354 mIm.registerInputDeviceListener(this, null); 355 } 356 unRegisterInputDeviceListener()357 private void unRegisterInputDeviceListener() { 358 InputManager mIm = getSystemService(InputManager.class); 359 if (mIm == null) { 360 return; 361 } 362 mIm.unregisterInputDeviceListener(this); 363 } 364 unregisterContentMonitors()365 private void unregisterContentMonitors() { 366 mSettingsPackageMonitor.unregister(); 367 mSettingsContentObserver.unregister(getContentResolver()); 368 } 369 updateServicePreferences()370 protected void updateServicePreferences() { 371 final AccessibilityManager a11yManager = AccessibilityManager.getInstance(getPrefContext()); 372 // Since services category is auto generated we have to do a pass 373 // to generate it since services can come and go and then based on 374 // the global accessibility state to decided whether it is enabled. 375 for (Preference service : mServicePreferences) { 376 service.getParent().removePreference(service); 377 } 378 mServicePreferences.clear(); 379 380 initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER, 381 R.array.config_preinstalled_screen_reader_services); 382 initializePreBundledServicesMapFromArray(CATEGORY_CAPTIONS, 383 R.array.config_preinstalled_captions_services); 384 initializePreBundledServicesMapFromArray(CATEGORY_AUDIO, 385 R.array.config_preinstalled_audio_services); 386 initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY, 387 R.array.config_preinstalled_display_services); 388 initializePreBundledServicesMapFromArray(CATEGORY_SPEECH, 389 R.array.config_preinstalled_speech_services); 390 initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL, 391 R.array.config_preinstalled_interaction_control_services); 392 393 // ACCESSIBILITY_MENU_IN_SYSTEM is a default pre-bundled interaction control service. 394 // If the device opts out of including this service then this is a no-op. 395 mPreBundledServiceComponentToCategoryMap.put( 396 AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM, 397 mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)); 398 399 final List<AccessibilityShortcutInfo> installedShortcutList = 400 a11yManager.getInstalledAccessibilityShortcutListAsUser(getPrefContext(), 401 UserHandle.myUserId()); 402 final List<AccessibilityServiceInfo> installedServiceList = 403 a11yManager.getInstalledAccessibilityServiceList(); 404 final List<RestrictedPreference> preferenceList = getInstalledAccessibilityPreferences( 405 getPrefContext(), installedShortcutList, installedServiceList); 406 407 removeNonPreinstalledComponents(mPreBundledServiceComponentToCategoryMap, 408 installedShortcutList, installedServiceList); 409 410 final PreferenceCategory downloadedServicesCategory = 411 mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES); 412 413 for (int i = 0, count = preferenceList.size(); i < count; ++i) { 414 final RestrictedPreference preference = preferenceList.get(i); 415 final ComponentName componentName = preference.getExtras().getParcelable( 416 EXTRA_COMPONENT_NAME); 417 PreferenceCategory prefCategory = downloadedServicesCategory; 418 // Set the appropriate category if the service comes pre-installed. 419 if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) { 420 prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName); 421 } 422 prefCategory.addPreference(preference); 423 mServicePreferences.add(preference); 424 } 425 426 // Update the order of all the category according to the order defined in xml file. 427 updateCategoryOrderFromArray(CATEGORY_SCREEN_READER, 428 R.array.config_order_screen_reader_services); 429 updateCategoryOrderFromArray(CATEGORY_CAPTIONS, 430 R.array.config_order_captions_services); 431 updateCategoryOrderFromArray(CATEGORY_AUDIO, 432 R.array.config_order_audio_services); 433 updateCategoryOrderFromArray(CATEGORY_INTERACTION_CONTROL, 434 R.array.config_order_interaction_control_services); 435 updateCategoryOrderFromArray(CATEGORY_DISPLAY, 436 R.array.config_order_display_services); 437 updateCategoryOrderFromArray(CATEGORY_SPEECH, 438 R.array.config_order_speech_services); 439 440 // Need to check each time when updateServicePreferences() called. 441 if (downloadedServicesCategory.getPreferenceCount() == 0) { 442 getPreferenceScreen().removePreference(downloadedServicesCategory); 443 } else { 444 getPreferenceScreen().addPreference(downloadedServicesCategory); 445 } 446 447 // Hide category if it is empty. 448 updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER); 449 updatePreferenceCategoryVisibility(CATEGORY_SPEECH); 450 } 451 452 /** 453 * Gets a list of {@link RestrictedPreference}s for the provided a11y shortcuts and services. 454 * 455 * <p>{@code modifiableInstalledServiceList} may be modified to remove any entries with 456 * matching package name and label as an entry in {@code installedShortcutList}. 457 * 458 * @param installedShortcutList A list of installed {@link AccessibilityShortcutInfo}s. 459 * @param installedServiceList A list of installed {@link AccessibilityServiceInfo}s. 460 */ getInstalledAccessibilityPreferences(Context context, List<AccessibilityShortcutInfo> installedShortcutList, List<AccessibilityServiceInfo> installedServiceList)461 private static List<RestrictedPreference> getInstalledAccessibilityPreferences(Context context, 462 List<AccessibilityShortcutInfo> installedShortcutList, 463 List<AccessibilityServiceInfo> installedServiceList) { 464 final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context); 465 466 final List<AccessibilityActivityPreference> activityList = 467 preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList); 468 final List<RestrictedPreference> serviceList = 469 preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList); 470 471 final List<RestrictedPreference> preferenceList = new ArrayList<>(); 472 preferenceList.addAll(activityList); 473 preferenceList.addAll(serviceList); 474 475 return preferenceList; 476 } 477 removeNonPreinstalledComponents( Map<ComponentName, PreferenceCategory> componentToCategory, List<AccessibilityShortcutInfo> shortcutInfos, List<AccessibilityServiceInfo> serviceInfos)478 private static void removeNonPreinstalledComponents( 479 Map<ComponentName, PreferenceCategory> componentToCategory, 480 List<AccessibilityShortcutInfo> shortcutInfos, 481 List<AccessibilityServiceInfo> serviceInfos) { 482 for (AccessibilityShortcutInfo info : shortcutInfos) { 483 if (!info.getActivityInfo().applicationInfo.isSystemApp()) { 484 componentToCategory.remove(info.getComponentName()); 485 } 486 } 487 for (AccessibilityServiceInfo info : serviceInfos) { 488 if (!info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) { 489 componentToCategory.remove(info.getComponentName()); 490 } 491 } 492 } 493 initializePreBundledServicesMapFromArray(String categoryKey, int key)494 private void initializePreBundledServicesMapFromArray(String categoryKey, int key) { 495 String[] services = getResources().getStringArray(key); 496 PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 497 for (int i = 0; i < services.length; i++) { 498 ComponentName component = ComponentName.unflattenFromString(services[i]); 499 mPreBundledServiceComponentToCategoryMap.put(component, category); 500 } 501 } 502 503 /** 504 * Update the order of preferences in the category by matching their preference 505 * key with the string array of preference order which is defined in the xml. 506 * 507 * @param categoryKey The key of the category need to update the order 508 * @param key The key of the string array which defines the order of category 509 */ updateCategoryOrderFromArray(String categoryKey, int key)510 private void updateCategoryOrderFromArray(String categoryKey, int key) { 511 String[] services = getResources().getStringArray(key); 512 PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 513 int preferenceCount = category.getPreferenceCount(); 514 int serviceLength = services.length; 515 for (int preferenceIndex = 0; preferenceIndex < preferenceCount; preferenceIndex++) { 516 for (int serviceIndex = 0; serviceIndex < serviceLength; serviceIndex++) { 517 if (category.getPreference(preferenceIndex).getKey() 518 .equals(services[serviceIndex])) { 519 category.getPreference(preferenceIndex).setOrder(serviceIndex); 520 break; 521 } 522 } 523 } 524 } 525 526 /** 527 * Updates the visibility of a category according to its child preference count. 528 * 529 * @param categoryKey The key of the category which needs to check 530 */ updatePreferenceCategoryVisibility(String categoryKey)531 private void updatePreferenceCategoryVisibility(String categoryKey) { 532 final PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 533 category.setVisible(category.getPreferenceCount() != 0); 534 } 535 536 /** 537 * Updates preferences related to system configurations. 538 */ updateSystemPreferences()539 protected void updateSystemPreferences() {} 540 updatePreferencesState()541 private void updatePreferencesState() { 542 final List<AbstractPreferenceController> controllers = new ArrayList<>(); 543 getPreferenceControllers().forEach(controllers::addAll); 544 controllers.forEach(controller -> controller.updateState( 545 findPreference(controller.getPreferenceKey()))); 546 } 547 548 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 549 new BaseSearchIndexProvider(R.xml.accessibility_settings) { 550 @Override 551 public List<SearchIndexableRaw> getRawDataToIndex(Context context, 552 boolean enabled) { 553 return FeatureFactory.getFeatureFactory() 554 .getAccessibilitySearchFeatureProvider().getSearchIndexableRawData( 555 context); 556 } 557 558 @Override 559 public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, 560 boolean enabled) { 561 List<SearchIndexableRaw> dynamicRawData = super.getDynamicRawDataToIndex( 562 context, enabled); 563 if (dynamicRawData == null) { 564 dynamicRawData = new ArrayList<>(); 565 } 566 if (!Flags.fixA11ySettingsSearch()) { 567 return dynamicRawData; 568 } 569 570 AccessibilityManager a11yManager = context.getSystemService( 571 AccessibilityManager.class); 572 AccessibilitySearchFeatureProvider a11ySearchFeatureProvider = 573 FeatureFactory.getFeatureFactory() 574 .getAccessibilitySearchFeatureProvider(); 575 List<RestrictedPreference> installedA11yFeaturesPref = 576 AccessibilitySettings.getInstalledAccessibilityPreferences( 577 context, 578 a11yManager.getInstalledAccessibilityShortcutListAsUser( 579 context, UserHandle.myUserId()), 580 a11yManager.getInstalledAccessibilityServiceList() 581 ); 582 for (RestrictedPreference pref : installedA11yFeaturesPref) { 583 SearchIndexableRaw indexableRaw = new SearchIndexableRaw(context); 584 indexableRaw.key = pref.getKey(); 585 indexableRaw.title = pref.getTitle().toString(); 586 @NonNull String synonyms = ""; 587 if (pref instanceof AccessibilityServicePreference) { 588 synonyms = a11ySearchFeatureProvider.getSynonymsForComponent( 589 context, 590 ((AccessibilityServicePreference) pref).getComponentName()); 591 } else if (pref instanceof AccessibilityActivityPreference) { 592 synonyms = a11ySearchFeatureProvider.getSynonymsForComponent( 593 context, 594 ((AccessibilityActivityPreference) pref).getComponentName()); 595 } 596 indexableRaw.keywords = synonyms; 597 dynamicRawData.add(indexableRaw); 598 } 599 600 return dynamicRawData; 601 } 602 }; 603 604 @Override onInputDeviceAdded(int deviceId)605 public void onInputDeviceAdded(int deviceId) { 606 } 607 608 @Override onInputDeviceRemoved(int deviceId)609 public void onInputDeviceRemoved(int deviceId) { 610 } 611 612 @Override onInputDeviceChanged(int deviceId)613 public void onInputDeviceChanged(int deviceId) { 614 mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS); 615 } 616 } 617