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.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM; 20 21 import android.accessibilityservice.AccessibilityServiceInfo; 22 import android.accessibilityservice.AccessibilityShortcutInfo; 23 import android.app.admin.DevicePolicyManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.ActivityInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ResolveInfo; 30 import android.content.pm.ServiceInfo; 31 import android.graphics.drawable.Drawable; 32 import android.hardware.display.ColorDisplayManager; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.UserHandle; 37 import android.provider.Settings; 38 import android.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.view.accessibility.AccessibilityManager; 41 42 import androidx.annotation.VisibleForTesting; 43 import androidx.core.content.ContextCompat; 44 import androidx.preference.Preference; 45 import androidx.preference.PreferenceCategory; 46 import androidx.preference.SwitchPreference; 47 48 import com.android.internal.accessibility.AccessibilityShortcutController; 49 import com.android.internal.content.PackageMonitor; 50 import com.android.settings.R; 51 import com.android.settings.Utils; 52 import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType; 53 import com.android.settings.dashboard.DashboardFragment; 54 import com.android.settings.display.DarkUIPreferenceController; 55 import com.android.settings.search.BaseSearchIndexProvider; 56 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 57 import com.android.settingslib.RestrictedLockUtilsInternal; 58 import com.android.settingslib.RestrictedPreference; 59 import com.android.settingslib.accessibility.AccessibilityUtils; 60 import com.android.settingslib.search.SearchIndexable; 61 62 import java.util.ArrayList; 63 import java.util.Collection; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 68 /** Activity with the accessibility settings. */ 69 @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) 70 public class AccessibilitySettings extends DashboardFragment { 71 72 private static final String TAG = "AccessibilitySettings"; 73 74 // Index of the first preference in a preference category. 75 private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1; 76 77 // Preference categories 78 private static final String CATEGORY_SCREEN_READER = "screen_reader_category"; 79 private static final String CATEGORY_AUDIO_AND_CAPTIONS = "audio_and_captions_category"; 80 private static final String CATEGORY_DISPLAY = "display_category"; 81 private static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category"; 82 private static final String CATEGORY_EXPERIMENTAL = "experimental_category"; 83 private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category"; 84 85 private static final String[] CATEGORIES = new String[] { 86 CATEGORY_SCREEN_READER, CATEGORY_AUDIO_AND_CAPTIONS, CATEGORY_DISPLAY, 87 CATEGORY_INTERACTION_CONTROL, CATEGORY_EXPERIMENTAL, CATEGORY_DOWNLOADED_SERVICES 88 }; 89 90 // Preferences 91 private static final String TOGGLE_INVERSION_PREFERENCE = 92 "toggle_inversion_preference"; 93 private static final String TOGGLE_LARGE_POINTER_ICON = 94 "toggle_large_pointer_icon"; 95 private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations"; 96 private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN = 97 "magnification_preference_screen"; 98 private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = 99 "daltonizer_preference"; 100 101 // Extras passed to sub-fragments. 102 static final String EXTRA_PREFERENCE_KEY = "preference_key"; 103 static final String EXTRA_CHECKED = "checked"; 104 static final String EXTRA_TITLE = "title"; 105 static final String EXTRA_TITLE_RES = "title_res"; 106 static final String EXTRA_RESOLVE_INFO = "resolve_info"; 107 static final String EXTRA_SUMMARY = "summary"; 108 static final String EXTRA_SETTINGS_TITLE = "settings_title"; 109 static final String EXTRA_COMPONENT_NAME = "component_name"; 110 static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name"; 111 static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource"; 112 static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw"; 113 static final String EXTRA_ANIMATED_IMAGE_RES = "animated_image_res"; 114 static final String EXTRA_HTML_DESCRIPTION = "html_description"; 115 116 // Timeout before we update the services if packages are added/removed 117 // since the AccessibilityManagerService has to do that processing first 118 // to generate the AccessibilityServiceInfo we need for proper 119 // presentation. 120 private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; 121 122 private final Handler mHandler = new Handler(); 123 124 private final Runnable mUpdateRunnable = new Runnable() { 125 @Override 126 public void run() { 127 if (getActivity() != null) { 128 updateServicePreferences(); 129 } 130 } 131 }; 132 133 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 134 @Override 135 public void onPackageAdded(String packageName, int uid) { 136 sendUpdate(); 137 } 138 139 @Override 140 public void onPackageAppeared(String packageName, int reason) { 141 sendUpdate(); 142 } 143 144 @Override 145 public void onPackageDisappeared(String packageName, int reason) { 146 sendUpdate(); 147 } 148 149 @Override 150 public void onPackageRemoved(String packageName, int uid) { 151 sendUpdate(); 152 } 153 154 private void sendUpdate() { 155 mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS); 156 } 157 }; 158 159 private final SettingsContentObserver mSettingsContentObserver; 160 161 private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap = 162 new ArrayMap<>(); 163 private final Map<Preference, PreferenceCategory> mServicePreferenceToPreferenceCategoryMap = 164 new ArrayMap<>(); 165 private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap = 166 new ArrayMap<>(); 167 168 private SwitchPreference mToggleLargePointerIconPreference; 169 private SwitchPreference mToggleDisableAnimationsPreference; 170 private Preference mDisplayMagnificationPreferenceScreen; 171 private Preference mDisplayDaltonizerPreferenceScreen; 172 private Preference mToggleInversionPreference; 173 174 /** 175 * Check if the color transforms are color accelerated. Some transforms are experimental only 176 * on non-accelerated platforms due to the performance implications. 177 * 178 * @param context The current context 179 */ isColorTransformAccelerated(Context context)180 public static boolean isColorTransformAccelerated(Context context) { 181 return context.getResources() 182 .getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated); 183 } 184 AccessibilitySettings()185 public AccessibilitySettings() { 186 // Observe changes to anything that the shortcut can toggle, so we can reflect updates 187 final Collection<AccessibilityShortcutController.ToggleableFrameworkFeatureInfo> features = 188 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap().values(); 189 final List<String> shortcutFeatureKeys = new ArrayList<>(features.size()); 190 for (AccessibilityShortcutController.ToggleableFrameworkFeatureInfo feature : features) { 191 shortcutFeatureKeys.add(feature.getSettingKey()); 192 } 193 194 // Observe changes from accessibility selection menu 195 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); 196 shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); 197 mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) { 198 @Override 199 public void onChange(boolean selfChange, Uri uri) { 200 updateAllPreferences(); 201 } 202 }; 203 } 204 205 @Override getMetricsCategory()206 public int getMetricsCategory() { 207 return SettingsEnums.ACCESSIBILITY; 208 } 209 210 @Override getHelpResource()211 public int getHelpResource() { 212 return R.string.help_uri_accessibility; 213 } 214 215 @Override onCreate(Bundle icicle)216 public void onCreate(Bundle icicle) { 217 super.onCreate(icicle); 218 initializeAllPreferences(); 219 } 220 221 @Override onAttach(Context context)222 public void onAttach(Context context) { 223 super.onAttach(context); 224 use(DarkUIPreferenceController.class).setParentFragment(this); 225 use(AccessibilityHearingAidPreferenceController.class) 226 .setFragmentManager(getFragmentManager()); 227 } 228 229 @Override onStart()230 public void onStart() { 231 super.onStart(); 232 updateAllPreferences(); 233 234 mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false); 235 mSettingsContentObserver.register(getContentResolver()); 236 } 237 238 @Override onStop()239 public void onStop() { 240 mSettingsPackageMonitor.unregister(); 241 mSettingsContentObserver.unregister(getContentResolver()); 242 super.onStop(); 243 } 244 245 @Override getPreferenceScreenResId()246 protected int getPreferenceScreenResId() { 247 return R.xml.accessibility_settings; 248 } 249 250 @Override getLogTag()251 protected String getLogTag() { 252 return TAG; 253 } 254 255 /** 256 * Returns the summary for the current state of this accessibilityService. 257 * 258 * @param context A valid context 259 * @param info The accessibilityService's info 260 * @param serviceEnabled Whether the accessibility service is enabled. 261 * @return The service summary 262 */ 263 @VisibleForTesting getServiceSummary(Context context, AccessibilityServiceInfo info, boolean serviceEnabled)264 static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info, 265 boolean serviceEnabled) { 266 if (serviceEnabled && info.crashed) { 267 return context.getText(R.string.accessibility_summary_state_stopped); 268 } 269 270 final CharSequence serviceState; 271 final int fragmentType = AccessibilityUtil.getAccessibilityServiceFragmentType(info); 272 if (fragmentType == AccessibilityServiceFragmentType.INVISIBLE_TOGGLE) { 273 final ComponentName componentName = new ComponentName( 274 info.getResolveInfo().serviceInfo.packageName, 275 info.getResolveInfo().serviceInfo.name); 276 final boolean shortcutEnabled = AccessibilityUtil.getUserShortcutTypesFromSettings( 277 context, componentName) != AccessibilityUtil.UserShortcutType.EMPTY; 278 serviceState = shortcutEnabled 279 ? context.getText(R.string.accessibility_summary_shortcut_enabled) 280 : context.getText(R.string.accessibility_summary_shortcut_disabled); 281 } else { 282 serviceState = serviceEnabled 283 ? context.getText(R.string.accessibility_summary_state_enabled) 284 : context.getText(R.string.accessibility_summary_state_disabled); 285 } 286 287 final CharSequence serviceSummary = info.loadSummary(context.getPackageManager()); 288 final String stateSummaryCombo = context.getString( 289 R.string.preference_summary_default_combination, 290 serviceState, serviceSummary); 291 292 return TextUtils.isEmpty(serviceSummary) ? serviceState : stateSummaryCombo; 293 } 294 295 /** 296 * Returns the description for the current state of this accessibilityService. 297 * 298 * @param context A valid context 299 * @param info The accessibilityService's info 300 * @param serviceEnabled Whether the accessibility service is enabled. 301 * @return The service description 302 */ 303 @VisibleForTesting getServiceDescription(Context context, AccessibilityServiceInfo info, boolean serviceEnabled)304 static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info, 305 boolean serviceEnabled) { 306 if (serviceEnabled && info.crashed) { 307 return context.getText(R.string.accessibility_description_state_stopped); 308 } 309 310 return info.loadDescription(context.getPackageManager()); 311 } 312 isRampingRingerEnabled(final Context context)313 static boolean isRampingRingerEnabled(final Context context) { 314 return Settings.Global.getInt( 315 context.getContentResolver(), Settings.Global.APPLY_RAMPING_RINGER, 0) == 1; 316 } 317 initializeAllPreferences()318 private void initializeAllPreferences() { 319 for (int i = 0; i < CATEGORIES.length; i++) { 320 PreferenceCategory prefCategory = findPreference(CATEGORIES[i]); 321 mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory); 322 } 323 324 // Display inversion. 325 mToggleInversionPreference = findPreference(TOGGLE_INVERSION_PREFERENCE); 326 327 // Large pointer icon. 328 mToggleLargePointerIconPreference = findPreference(TOGGLE_LARGE_POINTER_ICON); 329 330 mToggleDisableAnimationsPreference = findPreference(TOGGLE_DISABLE_ANIMATIONS); 331 332 // Display magnification. 333 mDisplayMagnificationPreferenceScreen = findPreference( 334 DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN); 335 336 // Display color adjustments. 337 mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN); 338 } 339 updateAllPreferences()340 private void updateAllPreferences() { 341 updateSystemPreferences(); 342 updateServicePreferences(); 343 } 344 updateServicePreferences()345 protected void updateServicePreferences() { 346 // Since services category is auto generated we have to do a pass 347 // to generate it since services can come and go and then based on 348 // the global accessibility state to decided whether it is enabled. 349 final ArrayList<Preference> servicePreferences = 350 new ArrayList<>(mServicePreferenceToPreferenceCategoryMap.keySet()); 351 for (int i = 0; i < servicePreferences.size(); i++) { 352 Preference service = servicePreferences.get(i); 353 PreferenceCategory category = mServicePreferenceToPreferenceCategoryMap.get(service); 354 category.removePreference(service); 355 } 356 357 initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER, 358 R.array.config_preinstalled_screen_reader_services); 359 initializePreBundledServicesMapFromArray(CATEGORY_AUDIO_AND_CAPTIONS, 360 R.array.config_preinstalled_audio_and_caption_services); 361 initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY, 362 R.array.config_preinstalled_display_services); 363 initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL, 364 R.array.config_preinstalled_interaction_control_services); 365 366 final List<RestrictedPreference> preferenceList = getInstalledAccessibilityList( 367 getPrefContext()); 368 369 final PreferenceCategory downloadedServicesCategory = 370 mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES); 371 372 for (int i = 0, count = preferenceList.size(); i < count; ++i) { 373 final RestrictedPreference preference = preferenceList.get(i); 374 final ComponentName componentName = preference.getExtras().getParcelable( 375 EXTRA_COMPONENT_NAME); 376 PreferenceCategory prefCategory = downloadedServicesCategory; 377 // Set the appropriate category if the service comes pre-installed. 378 if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) { 379 prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName); 380 } 381 prefCategory.addPreference(preference); 382 mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory); 383 } 384 385 // Update the order of all the category according to the order defined in xml file. 386 updateCategoryOrderFromArray(CATEGORY_SCREEN_READER, 387 R.array.config_order_screen_reader_services); 388 updateCategoryOrderFromArray(CATEGORY_AUDIO_AND_CAPTIONS, 389 R.array.config_order_audio_and_caption_services); 390 updateCategoryOrderFromArray(CATEGORY_INTERACTION_CONTROL, 391 R.array.config_order_interaction_control_services); 392 updateCategoryOrderFromArray(CATEGORY_DISPLAY, 393 R.array.config_order_display_services); 394 395 // Need to check each time when updateServicePreferences() called. 396 if (downloadedServicesCategory.getPreferenceCount() == 0) { 397 getPreferenceScreen().removePreference(downloadedServicesCategory); 398 } else { 399 getPreferenceScreen().addPreference(downloadedServicesCategory); 400 } 401 } 402 getInstalledAccessibilityList(Context context)403 private List<RestrictedPreference> getInstalledAccessibilityList(Context context) { 404 final AccessibilityManager a11yManager = AccessibilityManager.getInstance(context); 405 final RestrictedPreferenceHelper preferenceHelper = new RestrictedPreferenceHelper(context); 406 407 final List<AccessibilityShortcutInfo> installedShortcutList = 408 a11yManager.getInstalledAccessibilityShortcutListAsUser(context, 409 UserHandle.myUserId()); 410 411 // Remove duplicate item here, new a ArrayList to copy unmodifiable list result 412 // (getInstalledAccessibilityServiceList). 413 final List<AccessibilityServiceInfo> installedServiceList = new ArrayList<>( 414 a11yManager.getInstalledAccessibilityServiceList()); 415 installedServiceList.removeIf( 416 target -> containsTargetNameInList(installedShortcutList, target)); 417 418 final List<RestrictedPreference> activityList = 419 preferenceHelper.createAccessibilityActivityPreferenceList(installedShortcutList); 420 421 final List<RestrictedPreference> serviceList = 422 preferenceHelper.createAccessibilityServicePreferenceList(installedServiceList); 423 424 final List<RestrictedPreference> preferenceList = new ArrayList<>(); 425 preferenceList.addAll(activityList); 426 preferenceList.addAll(serviceList); 427 428 return preferenceList; 429 } 430 containsTargetNameInList(List<AccessibilityShortcutInfo> shortcutInfos, AccessibilityServiceInfo targetServiceInfo)431 private boolean containsTargetNameInList(List<AccessibilityShortcutInfo> shortcutInfos, 432 AccessibilityServiceInfo targetServiceInfo) { 433 final ServiceInfo serviceInfo = targetServiceInfo.getResolveInfo().serviceInfo; 434 final String servicePackageName = serviceInfo.packageName; 435 final CharSequence serviceLabel = serviceInfo.loadLabel(getPackageManager()); 436 437 for (int i = 0, count = shortcutInfos.size(); i < count; ++i) { 438 final ActivityInfo activityInfo = shortcutInfos.get(i).getActivityInfo(); 439 final String activityPackageName = activityInfo.packageName; 440 final CharSequence activityLabel = activityInfo.loadLabel(getPackageManager()); 441 if (servicePackageName.equals(activityPackageName) 442 && serviceLabel.equals(activityLabel)) { 443 return true; 444 } 445 } 446 return false; 447 } 448 initializePreBundledServicesMapFromArray(String categoryKey, int key)449 private void initializePreBundledServicesMapFromArray(String categoryKey, int key) { 450 String[] services = getResources().getStringArray(key); 451 PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 452 for (int i = 0; i < services.length; i++) { 453 ComponentName component = ComponentName.unflattenFromString(services[i]); 454 mPreBundledServiceComponentToCategoryMap.put(component, category); 455 } 456 } 457 458 /** 459 * Update the order of preferences in the category by matching their preference 460 * key with the string array of preference order which is defined in the xml. 461 * 462 * @param categoryKey The key of the category need to update the order 463 * @param key The key of the string array which defines the order of category 464 */ updateCategoryOrderFromArray(String categoryKey, int key)465 private void updateCategoryOrderFromArray(String categoryKey, int key) { 466 String[] services = getResources().getStringArray(key); 467 PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 468 int preferenceCount = category.getPreferenceCount(); 469 int serviceLength = services.length; 470 for (int preferenceIndex = 0; preferenceIndex < preferenceCount; preferenceIndex++) { 471 for (int serviceIndex = 0; serviceIndex < serviceLength; serviceIndex++) { 472 if (category.getPreference(preferenceIndex).getKey() 473 .equals(services[serviceIndex])) { 474 category.getPreference(preferenceIndex).setOrder(serviceIndex); 475 break; 476 } 477 } 478 } 479 } 480 updateSystemPreferences()481 protected void updateSystemPreferences() { 482 // Move color inversion and color correction preferences to Display category if device 483 // supports HWC hardware-accelerated color transform. 484 if (ColorDisplayManager.isColorTransformAccelerated(getContext())) { 485 PreferenceCategory experimentalCategory = 486 mCategoryToPrefCategoryMap.get(CATEGORY_EXPERIMENTAL); 487 PreferenceCategory displayCategory = 488 mCategoryToPrefCategoryMap.get(CATEGORY_DISPLAY); 489 experimentalCategory.removePreference(mToggleInversionPreference); 490 experimentalCategory.removePreference(mDisplayDaltonizerPreferenceScreen); 491 mDisplayMagnificationPreferenceScreen.setSummary( 492 ToggleScreenMagnificationPreferenceFragment.getServiceSummary(getContext())); 493 mDisplayDaltonizerPreferenceScreen.setOrder( 494 mDisplayMagnificationPreferenceScreen.getOrder() + 1); 495 mDisplayDaltonizerPreferenceScreen.setSummary(AccessibilityUtil.getSummary( 496 getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED)); 497 mToggleInversionPreference.setOrder( 498 mDisplayDaltonizerPreferenceScreen.getOrder() + 1); 499 mToggleLargePointerIconPreference.setOrder( 500 mToggleInversionPreference.getOrder() + 1); 501 mToggleDisableAnimationsPreference.setOrder( 502 mToggleLargePointerIconPreference.getOrder() + 1); 503 mToggleInversionPreference.setSummary(AccessibilityUtil.getSummary( 504 getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED)); 505 displayCategory.addPreference(mToggleInversionPreference); 506 displayCategory.addPreference(mDisplayDaltonizerPreferenceScreen); 507 } 508 } 509 510 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 511 new BaseSearchIndexProvider(R.xml.accessibility_settings); 512 513 /** 514 * This class helps setup RestrictedPreference. 515 */ 516 @VisibleForTesting 517 static class RestrictedPreferenceHelper { 518 private final Context mContext; 519 private final DevicePolicyManager mDpm; 520 private final PackageManager mPm; 521 RestrictedPreferenceHelper(Context context)522 RestrictedPreferenceHelper(Context context) { 523 mContext = context; 524 mDpm = context.getSystemService(DevicePolicyManager.class); 525 mPm = context.getPackageManager(); 526 } 527 528 /** 529 * Creates the list of {@link RestrictedPreference} with the installedServices arguments. 530 * 531 * @param installedServices The list of {@link AccessibilityServiceInfo}s of the 532 * installed accessibility services 533 * @return The list of {@link RestrictedPreference} 534 */ createAccessibilityServicePreferenceList( List<AccessibilityServiceInfo> installedServices)535 List<RestrictedPreference> createAccessibilityServicePreferenceList( 536 List<AccessibilityServiceInfo> installedServices) { 537 538 final Set<ComponentName> enabledServices = 539 AccessibilityUtils.getEnabledServicesFromSettings(mContext); 540 final List<String> permittedServices = mDpm.getPermittedAccessibilityServices( 541 UserHandle.myUserId()); 542 final int installedServicesSize = installedServices.size(); 543 544 final List<RestrictedPreference> preferenceList = new ArrayList<>( 545 installedServicesSize); 546 547 for (int i = 0; i < installedServicesSize; ++i) { 548 final AccessibilityServiceInfo info = installedServices.get(i); 549 final ResolveInfo resolveInfo = info.getResolveInfo(); 550 final String packageName = resolveInfo.serviceInfo.packageName; 551 final ComponentName componentName = new ComponentName(packageName, 552 resolveInfo.serviceInfo.name); 553 554 final String key = componentName.flattenToString(); 555 final CharSequence title = resolveInfo.loadLabel(mPm); 556 final boolean serviceEnabled = enabledServices.contains(componentName); 557 final CharSequence summary = getServiceSummary(mContext, info, serviceEnabled); 558 final String fragment = getAccessibilityServiceFragmentTypeName(info); 559 560 Drawable icon = resolveInfo.loadIcon(mPm); 561 if (resolveInfo.getIconResource() == 0) { 562 icon = ContextCompat.getDrawable(mContext, 563 R.drawable.ic_accessibility_generic); 564 } 565 566 final RestrictedPreference preference = createRestrictedPreference(key, title, 567 summary, icon, fragment); 568 569 // permittedServices null means all accessibility services are allowed. 570 final boolean serviceAllowed = 571 permittedServices == null || permittedServices.contains(packageName); 572 573 setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed, 574 serviceEnabled); 575 576 final String prefKey = preference.getKey(); 577 final int imageRes = info.getAnimatedImageRes(); 578 final CharSequence description = getServiceDescription(mContext, info, 579 serviceEnabled); 580 final String htmlDescription = info.loadHtmlDescription(mPm); 581 final String settingsClassName = info.getSettingsActivityName(); 582 583 putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription, 584 componentName); 585 putServiceExtras(preference, resolveInfo, serviceEnabled); 586 putSettingsExtras(preference, packageName, settingsClassName); 587 588 preferenceList.add(preference); 589 } 590 return preferenceList; 591 } 592 593 /** 594 * Create the list of {@link RestrictedPreference} with the installedShortcuts arguments. 595 * 596 * @param installedShortcuts The list of {@link AccessibilityShortcutInfo}s of the 597 * installed accessibility shortcuts 598 * @return The list of {@link RestrictedPreference} 599 */ createAccessibilityActivityPreferenceList( List<AccessibilityShortcutInfo> installedShortcuts)600 List<RestrictedPreference> createAccessibilityActivityPreferenceList( 601 List<AccessibilityShortcutInfo> installedShortcuts) { 602 final Set<ComponentName> enabledServices = 603 AccessibilityUtils.getEnabledServicesFromSettings(mContext); 604 final List<String> permittedServices = mDpm.getPermittedAccessibilityServices( 605 UserHandle.myUserId()); 606 607 final int installedShortcutsSize = installedShortcuts.size(); 608 final List<RestrictedPreference> preferenceList = new ArrayList<>( 609 installedShortcutsSize); 610 611 for (int i = 0; i < installedShortcutsSize; ++i) { 612 final AccessibilityShortcutInfo info = installedShortcuts.get(i); 613 final ActivityInfo activityInfo = info.getActivityInfo(); 614 final ComponentName componentName = info.getComponentName(); 615 616 final String key = componentName.flattenToString(); 617 final CharSequence title = activityInfo.loadLabel(mPm); 618 final String summary = info.loadSummary(mPm); 619 final String fragment = 620 LaunchAccessibilityActivityPreferenceFragment.class.getName(); 621 622 Drawable icon = activityInfo.loadIcon(mPm); 623 if (activityInfo.getIconResource() == 0) { 624 icon = ContextCompat.getDrawable(mContext, R.drawable.ic_accessibility_generic); 625 } 626 627 final RestrictedPreference preference = createRestrictedPreference(key, title, 628 summary, icon, fragment); 629 630 final String packageName = componentName.getPackageName(); 631 // permittedServices null means all accessibility services are allowed. 632 final boolean serviceAllowed = 633 permittedServices == null || permittedServices.contains(packageName); 634 final boolean serviceEnabled = enabledServices.contains(componentName); 635 636 setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed, 637 serviceEnabled); 638 639 final String prefKey = preference.getKey(); 640 final String description = info.loadDescription(mPm); 641 final int imageRes = info.getAnimatedImageRes(); 642 final String htmlDescription = info.loadHtmlDescription(mPm); 643 final String settingsClassName = info.getSettingsActivityName(); 644 645 putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription, 646 componentName); 647 putSettingsExtras(preference, packageName, settingsClassName); 648 649 preferenceList.add(preference); 650 } 651 return preferenceList; 652 } 653 getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info)654 private String getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info) { 655 // Shorten the name to avoid exceeding 100 characters in one line. 656 final String volumeShortcutToggleAccessibilityServicePreferenceFragment = 657 VolumeShortcutToggleAccessibilityServicePreferenceFragment.class.getName(); 658 659 switch (AccessibilityUtil.getAccessibilityServiceFragmentType(info)) { 660 case AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE: 661 return volumeShortcutToggleAccessibilityServicePreferenceFragment; 662 case AccessibilityServiceFragmentType.INVISIBLE_TOGGLE: 663 return InvisibleToggleAccessibilityServicePreferenceFragment.class.getName(); 664 case AccessibilityServiceFragmentType.TOGGLE: 665 return ToggleAccessibilityServicePreferenceFragment.class.getName(); 666 default: 667 // impossible status 668 throw new AssertionError(); 669 } 670 } 671 createRestrictedPreference(String key, CharSequence title, CharSequence summary, Drawable icon, String fragment)672 private RestrictedPreference createRestrictedPreference(String key, CharSequence title, 673 CharSequence summary, Drawable icon, String fragment) { 674 final RestrictedPreference preference = new RestrictedPreference(mContext); 675 676 preference.setKey(key); 677 preference.setTitle(title); 678 preference.setSummary(summary); 679 Utils.setSafeIcon(preference, icon); 680 preference.setFragment(fragment); 681 preference.setIconSize(ICON_SIZE_MEDIUM); 682 preference.setPersistent(false); // Disable SharedPreferences. 683 preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX); 684 685 return preference; 686 } 687 setRestrictedPreferenceEnabled(RestrictedPreference preference, String packageName, boolean serviceAllowed, boolean serviceEnabled)688 private void setRestrictedPreferenceEnabled(RestrictedPreference preference, 689 String packageName, boolean serviceAllowed, boolean serviceEnabled) { 690 if (serviceAllowed || serviceEnabled) { 691 preference.setEnabled(true); 692 } else { 693 // Disable accessibility service that are not permitted. 694 final EnforcedAdmin admin = 695 RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed( 696 mContext, packageName, UserHandle.myUserId()); 697 if (admin != null) { 698 preference.setDisabledByAdmin(admin); 699 } else { 700 preference.setEnabled(false); 701 } 702 } 703 } 704 705 /** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */ putBasicExtras(RestrictedPreference preference, String prefKey, CharSequence title, CharSequence summary, int imageRes, String htmlDescription, ComponentName componentName)706 private void putBasicExtras(RestrictedPreference preference, String prefKey, 707 CharSequence title, CharSequence summary, int imageRes, String htmlDescription, 708 ComponentName componentName) { 709 final Bundle extras = preference.getExtras(); 710 extras.putString(EXTRA_PREFERENCE_KEY, prefKey); 711 extras.putCharSequence(EXTRA_TITLE, title); 712 extras.putCharSequence(EXTRA_SUMMARY, summary); 713 extras.putParcelable(EXTRA_COMPONENT_NAME, componentName); 714 extras.putInt(EXTRA_ANIMATED_IMAGE_RES, imageRes); 715 extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription); 716 } 717 718 /** 719 * Puts the service extras into {@link RestrictedPreference}'s getExtras(). 720 * 721 * Called by {@link AccessibilityServiceInfo} for now. 722 */ putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo, Boolean serviceEnabled)723 private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo, 724 Boolean serviceEnabled) { 725 final Bundle extras = preference.getExtras(); 726 727 extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo); 728 extras.putBoolean(EXTRA_CHECKED, serviceEnabled); 729 } 730 731 /** 732 * Puts the settings extras into {@link RestrictedPreference}'s getExtras(). 733 * 734 * Called when settings UI is needed. 735 */ putSettingsExtras(RestrictedPreference preference, String packageName, String settingsClassName)736 private void putSettingsExtras(RestrictedPreference preference, String packageName, 737 String settingsClassName) { 738 final Bundle extras = preference.getExtras(); 739 740 if (!TextUtils.isEmpty(settingsClassName)) { 741 extras.putString(EXTRA_SETTINGS_TITLE, 742 mContext.getText(R.string.accessibility_menu_item_settings).toString()); 743 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME, 744 new ComponentName(packageName, settingsClassName).flattenToString()); 745 } 746 } 747 } 748 } 749