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 android.os.Vibrator.VibrationIntensity; 20 21 import android.accessibilityservice.AccessibilityServiceInfo; 22 import android.app.admin.DevicePolicyManager; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.ServiceInfo; 27 import android.content.res.Resources; 28 import android.graphics.drawable.Drawable; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.os.UserHandle; 33 import android.os.Vibrator; 34 import android.provider.SearchIndexableResource; 35 import android.provider.Settings; 36 import android.support.annotation.VisibleForTesting; 37 import android.support.v14.preference.SwitchPreference; 38 import android.support.v4.content.ContextCompat; 39 import android.support.v7.preference.ListPreference; 40 import android.support.v7.preference.Preference; 41 import android.support.v7.preference.PreferenceCategory; 42 import android.support.v7.preference.PreferenceScreen; 43 import android.text.TextUtils; 44 import android.util.ArrayMap; 45 import android.view.KeyCharacterMap; 46 import android.view.KeyEvent; 47 import android.view.accessibility.AccessibilityManager; 48 49 import com.android.internal.accessibility.AccessibilityShortcutController; 50 import com.android.internal.content.PackageMonitor; 51 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 52 import com.android.internal.view.RotationPolicy; 53 import com.android.internal.view.RotationPolicy.RotationPolicyListener; 54 import com.android.settings.R; 55 import com.android.settings.SettingsPreferenceFragment; 56 import com.android.settings.Utils; 57 import com.android.settings.search.BaseSearchIndexProvider; 58 import com.android.settings.search.Indexable; 59 import com.android.settingslib.RestrictedLockUtils; 60 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 61 import com.android.settingslib.RestrictedPreference; 62 import com.android.settingslib.accessibility.AccessibilityUtils; 63 64 import java.util.ArrayList; 65 import java.util.Collection; 66 import java.util.HashMap; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Set; 70 71 /** 72 * Activity with the accessibility settings. 73 */ 74 public class AccessibilitySettings extends SettingsPreferenceFragment implements 75 Preference.OnPreferenceChangeListener, Indexable { 76 77 // Index of the first preference in a preference category. 78 private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1; 79 80 // Preference categories 81 private static final String CATEGORY_SCREEN_READER = "screen_reader_category"; 82 private static final String CATEGORY_AUDIO_AND_CAPTIONS = "audio_and_captions_category"; 83 private static final String CATEGORY_DISPLAY = "display_category"; 84 private static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category"; 85 private static final String CATEGORY_EXPERIMENTAL = "experimental_category"; 86 private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category"; 87 88 private static final String[] CATEGORIES = new String[] { 89 CATEGORY_SCREEN_READER, CATEGORY_AUDIO_AND_CAPTIONS, CATEGORY_DISPLAY, 90 CATEGORY_INTERACTION_CONTROL, CATEGORY_EXPERIMENTAL, CATEGORY_DOWNLOADED_SERVICES 91 }; 92 93 // Preferences 94 private static final String TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE = 95 "toggle_high_text_contrast_preference"; 96 private static final String TOGGLE_INVERSION_PREFERENCE = 97 "toggle_inversion_preference"; 98 private static final String TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE = 99 "toggle_power_button_ends_call_preference"; 100 private static final String TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE = 101 "toggle_lock_screen_rotation_preference"; 102 private static final String TOGGLE_LARGE_POINTER_ICON = 103 "toggle_large_pointer_icon"; 104 private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations"; 105 private static final String TOGGLE_MASTER_MONO = 106 "toggle_master_mono"; 107 private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE = 108 "select_long_press_timeout_preference"; 109 private static final String ACCESSIBILITY_SHORTCUT_PREFERENCE = 110 "accessibility_shortcut_preference"; 111 private static final String CAPTIONING_PREFERENCE_SCREEN = 112 "captioning_preference_screen"; 113 private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN = 114 "magnification_preference_screen"; 115 private static final String FONT_SIZE_PREFERENCE_SCREEN = 116 "font_size_preference_screen"; 117 private static final String TTS_SETTINGS_PREFERENCE = 118 "tts_settings_preference"; 119 private static final String AUTOCLICK_PREFERENCE_SCREEN = 120 "autoclick_preference_screen"; 121 private static final String VIBRATION_PREFERENCE_SCREEN = 122 "vibration_preference_screen"; 123 private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = 124 "daltonizer_preference_screen"; 125 126 // Extras passed to sub-fragments. 127 static final String EXTRA_PREFERENCE_KEY = "preference_key"; 128 static final String EXTRA_CHECKED = "checked"; 129 static final String EXTRA_TITLE = "title"; 130 static final String EXTRA_TITLE_RES = "title_res"; 131 static final String EXTRA_RESOLVE_INFO = "resolve_info"; 132 static final String EXTRA_SUMMARY = "summary"; 133 static final String EXTRA_SUMMARY_RES = "summary_res"; 134 static final String EXTRA_SETTINGS_TITLE = "settings_title"; 135 static final String EXTRA_COMPONENT_NAME = "component_name"; 136 static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name"; 137 static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource"; 138 static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw"; 139 140 // Timeout before we update the services if packages are added/removed 141 // since the AccessibilityManagerService has to do that processing first 142 // to generate the AccessibilityServiceInfo we need for proper 143 // presentation. 144 private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000; 145 146 // Settings that should be changed when toggling animations 147 private static final String[] TOGGLE_ANIMATION_TARGETS = { 148 Settings.Global.WINDOW_ANIMATION_SCALE, Settings.Global.TRANSITION_ANIMATION_SCALE, 149 Settings.Global.ANIMATOR_DURATION_SCALE 150 }; 151 private static final String ANIMATION_ON_VALUE = "1"; 152 private static final String ANIMATION_OFF_VALUE = "0"; 153 154 private final Map<String, String> mLongPressTimeoutValueToTitleMap = new HashMap<>(); 155 156 private final Handler mHandler = new Handler(); 157 158 private final Runnable mUpdateRunnable = new Runnable() { 159 @Override 160 public void run() { 161 if (getActivity() != null) { 162 updateServicePreferences(); 163 } 164 } 165 }; 166 167 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 168 @Override 169 public void onPackageAdded(String packageName, int uid) { 170 sendUpdate(); 171 } 172 173 @Override 174 public void onPackageAppeared(String packageName, int reason) { 175 sendUpdate(); 176 } 177 178 @Override 179 public void onPackageDisappeared(String packageName, int reason) { 180 sendUpdate(); 181 } 182 183 @Override 184 public void onPackageRemoved(String packageName, int uid) { 185 sendUpdate(); 186 } 187 188 private void sendUpdate() { 189 mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS); 190 } 191 }; 192 193 private final SettingsContentObserver mSettingsContentObserver; 194 195 private final RotationPolicyListener mRotationPolicyListener = new RotationPolicyListener() { 196 @Override 197 public void onChange() { 198 updateLockScreenRotationCheckbox(); 199 } 200 }; 201 202 private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap = 203 new ArrayMap<>(); 204 private final Map<Preference, PreferenceCategory> mServicePreferenceToPreferenceCategoryMap = 205 new ArrayMap<>(); 206 private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap = 207 new ArrayMap<>(); 208 209 private SwitchPreference mToggleHighTextContrastPreference; 210 private SwitchPreference mTogglePowerButtonEndsCallPreference; 211 private SwitchPreference mToggleLockScreenRotationPreference; 212 private SwitchPreference mToggleLargePointerIconPreference; 213 private SwitchPreference mToggleDisableAnimationsPreference; 214 private SwitchPreference mToggleMasterMonoPreference; 215 private ListPreference mSelectLongPressTimeoutPreference; 216 private Preference mCaptioningPreferenceScreen; 217 private Preference mDisplayMagnificationPreferenceScreen; 218 private Preference mFontSizePreferenceScreen; 219 private Preference mAutoclickPreferenceScreen; 220 private Preference mAccessibilityShortcutPreferenceScreen; 221 private Preference mDisplayDaltonizerPreferenceScreen; 222 private Preference mVibrationPreferenceScreen; 223 private SwitchPreference mToggleInversionPreference; 224 225 private int mLongPressTimeoutDefault; 226 227 private DevicePolicyManager mDpm; 228 229 /** 230 * Check if the color transforms are color accelerated. Some transforms are experimental only 231 * on non-accelerated platforms due to the performance implications. 232 * 233 * @param context The current context 234 */ 235 public static boolean isColorTransformAccelerated(Context context) { 236 return context.getResources() 237 .getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated); 238 } 239 240 public AccessibilitySettings() { 241 // Observe changes to anything that the shortcut can toggle, so we can reflect updates 242 final Collection<AccessibilityShortcutController.ToggleableFrameworkFeatureInfo> features = 243 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap().values(); 244 final List<String> shortcutFeatureKeys = new ArrayList<>(features.size()); 245 for (AccessibilityShortcutController.ToggleableFrameworkFeatureInfo feature : features) { 246 shortcutFeatureKeys.add(feature.getSettingKey()); 247 } 248 mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) { 249 @Override 250 public void onChange(boolean selfChange, Uri uri) { 251 updateAllPreferences(); 252 } 253 }; 254 } 255 256 @Override 257 public int getMetricsCategory() { 258 return MetricsEvent.ACCESSIBILITY; 259 } 260 261 @Override 262 public int getHelpResource() { 263 return R.string.help_uri_accessibility; 264 } 265 266 @Override 267 public void onCreate(Bundle icicle) { 268 super.onCreate(icicle); 269 addPreferencesFromResource(R.xml.accessibility_settings); 270 initializeAllPreferences(); 271 mDpm = (DevicePolicyManager) (getActivity() 272 .getSystemService(Context.DEVICE_POLICY_SERVICE)); 273 } 274 275 @Override 276 public void onResume() { 277 super.onResume(); 278 updateAllPreferences(); 279 280 mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false); 281 mSettingsContentObserver.register(getContentResolver()); 282 if (RotationPolicy.isRotationSupported(getActivity())) { 283 RotationPolicy.registerRotationPolicyListener(getActivity(), 284 mRotationPolicyListener); 285 } 286 } 287 288 @Override 289 public void onPause() { 290 mSettingsPackageMonitor.unregister(); 291 mSettingsContentObserver.unregister(getContentResolver()); 292 if (RotationPolicy.isRotationSupported(getActivity())) { 293 RotationPolicy.unregisterRotationPolicyListener(getActivity(), 294 mRotationPolicyListener); 295 } 296 super.onPause(); 297 } 298 299 @Override 300 public boolean onPreferenceChange(Preference preference, Object newValue) { 301 if (mSelectLongPressTimeoutPreference == preference) { 302 handleLongPressTimeoutPreferenceChange((String) newValue); 303 return true; 304 } else if (mToggleInversionPreference == preference) { 305 handleToggleInversionPreferenceChange((Boolean) newValue); 306 return true; 307 } 308 return false; 309 } 310 311 private void handleLongPressTimeoutPreferenceChange(String stringValue) { 312 Settings.Secure.putInt(getContentResolver(), 313 Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue)); 314 mSelectLongPressTimeoutPreference.setSummary( 315 mLongPressTimeoutValueToTitleMap.get(stringValue)); 316 } 317 318 private void handleToggleInversionPreferenceChange(boolean checked) { 319 Settings.Secure.putInt(getContentResolver(), 320 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, (checked ? 1 : 0)); 321 } 322 323 @Override 324 public boolean onPreferenceTreeClick(Preference preference) { 325 if (mToggleHighTextContrastPreference == preference) { 326 handleToggleTextContrastPreferenceClick(); 327 return true; 328 } else if (mTogglePowerButtonEndsCallPreference == preference) { 329 handleTogglePowerButtonEndsCallPreferenceClick(); 330 return true; 331 } else if (mToggleLockScreenRotationPreference == preference) { 332 handleLockScreenRotationPreferenceClick(); 333 return true; 334 } else if (mToggleLargePointerIconPreference == preference) { 335 handleToggleLargePointerIconPreferenceClick(); 336 return true; 337 } else if (mToggleDisableAnimationsPreference == preference) { 338 handleToggleDisableAnimations(); 339 return true; 340 } else if (mToggleMasterMonoPreference == preference) { 341 handleToggleMasterMonoPreferenceClick(); 342 return true; 343 } 344 return super.onPreferenceTreeClick(preference); 345 } 346 347 public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info, 348 boolean serviceEnabled) { 349 final String serviceState = serviceEnabled 350 ? context.getString(R.string.accessibility_summary_state_enabled) 351 : context.getString(R.string.accessibility_summary_state_disabled); 352 final CharSequence serviceSummary = info.loadSummary(context.getPackageManager()); 353 final String stateSummaryCombo = context.getString( 354 R.string.preference_summary_default_combination, 355 serviceState, serviceSummary); 356 357 return (TextUtils.isEmpty(serviceSummary)) 358 ? serviceState 359 : stateSummaryCombo; 360 } 361 362 private void handleToggleTextContrastPreferenceClick() { 363 Settings.Secure.putInt(getContentResolver(), 364 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 365 (mToggleHighTextContrastPreference.isChecked() ? 1 : 0)); 366 } 367 368 private void handleTogglePowerButtonEndsCallPreferenceClick() { 369 Settings.Secure.putInt(getContentResolver(), 370 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, 371 (mTogglePowerButtonEndsCallPreference.isChecked() 372 ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP 373 : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF)); 374 } 375 376 private void handleLockScreenRotationPreferenceClick() { 377 RotationPolicy.setRotationLockForAccessibility(getActivity(), 378 !mToggleLockScreenRotationPreference.isChecked()); 379 } 380 381 private void handleToggleLargePointerIconPreferenceClick() { 382 Settings.Secure.putInt(getContentResolver(), 383 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 384 mToggleLargePointerIconPreference.isChecked() ? 1 : 0); 385 } 386 387 private void handleToggleDisableAnimations() { 388 String newAnimationValue = mToggleDisableAnimationsPreference.isChecked() 389 ? ANIMATION_OFF_VALUE : ANIMATION_ON_VALUE; 390 for (String animationPreference : TOGGLE_ANIMATION_TARGETS) { 391 Settings.Global.putString(getContentResolver(), animationPreference, newAnimationValue); 392 } 393 } 394 395 private void handleToggleMasterMonoPreferenceClick() { 396 Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO, 397 mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT); 398 } 399 400 private void initializeAllPreferences() { 401 for (int i = 0; i < CATEGORIES.length; i++) { 402 PreferenceCategory prefCategory = (PreferenceCategory) findPreference(CATEGORIES[i]); 403 mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory); 404 } 405 406 // Text contrast. 407 mToggleHighTextContrastPreference = 408 (SwitchPreference) findPreference(TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE); 409 410 // Display inversion. 411 mToggleInversionPreference = (SwitchPreference) findPreference(TOGGLE_INVERSION_PREFERENCE); 412 mToggleInversionPreference.setOnPreferenceChangeListener(this); 413 414 // Power button ends calls. 415 mTogglePowerButtonEndsCallPreference = 416 (SwitchPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE); 417 if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) 418 || !Utils.isVoiceCapable(getActivity())) { 419 mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL) 420 .removePreference(mTogglePowerButtonEndsCallPreference); 421 } 422 423 // Lock screen rotation. 424 mToggleLockScreenRotationPreference = 425 (SwitchPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE); 426 if (!RotationPolicy.isRotationSupported(getActivity())) { 427 mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL) 428 .removePreference(mToggleLockScreenRotationPreference); 429 } 430 431 // Large pointer icon. 432 mToggleLargePointerIconPreference = 433 (SwitchPreference) findPreference(TOGGLE_LARGE_POINTER_ICON); 434 435 mToggleDisableAnimationsPreference = 436 (SwitchPreference) findPreference(TOGGLE_DISABLE_ANIMATIONS); 437 438 // Master Mono 439 mToggleMasterMonoPreference = 440 (SwitchPreference) findPreference(TOGGLE_MASTER_MONO); 441 442 // Long press timeout. 443 mSelectLongPressTimeoutPreference = 444 (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE); 445 mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this); 446 if (mLongPressTimeoutValueToTitleMap.size() == 0) { 447 String[] timeoutValues = getResources().getStringArray( 448 R.array.long_press_timeout_selector_values); 449 mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]); 450 String[] timeoutTitles = getResources().getStringArray( 451 R.array.long_press_timeout_selector_titles); 452 final int timeoutValueCount = timeoutValues.length; 453 for (int i = 0; i < timeoutValueCount; i++) { 454 mLongPressTimeoutValueToTitleMap.put(timeoutValues[i], timeoutTitles[i]); 455 } 456 } 457 458 // Captioning. 459 mCaptioningPreferenceScreen = findPreference(CAPTIONING_PREFERENCE_SCREEN); 460 461 // Display magnification. 462 mDisplayMagnificationPreferenceScreen = findPreference( 463 DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN); 464 configureMagnificationPreferenceIfNeeded(mDisplayMagnificationPreferenceScreen); 465 466 // Font size. 467 mFontSizePreferenceScreen = findPreference(FONT_SIZE_PREFERENCE_SCREEN); 468 469 // Autoclick after pointer stops. 470 mAutoclickPreferenceScreen = findPreference(AUTOCLICK_PREFERENCE_SCREEN); 471 472 // Display color adjustments. 473 mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN); 474 475 // Accessibility shortcut. 476 mAccessibilityShortcutPreferenceScreen = findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE); 477 478 // Vibrations. 479 mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN); 480 } 481 482 private void updateAllPreferences() { 483 updateSystemPreferences(); 484 updateServicePreferences(); 485 } 486 487 protected void updateServicePreferences() { 488 // Since services category is auto generated we have to do a pass 489 // to generate it since services can come and go and then based on 490 // the global accessibility state to decided whether it is enabled. 491 492 // Generate. 493 ArrayList<Preference> servicePreferences = 494 new ArrayList<>(mServicePreferenceToPreferenceCategoryMap.keySet()); 495 for (int i = 0; i < servicePreferences.size(); i++) { 496 Preference service = servicePreferences.get(i); 497 PreferenceCategory category = mServicePreferenceToPreferenceCategoryMap.get(service); 498 category.removePreference(service); 499 } 500 501 initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER, 502 R.array.config_preinstalled_screen_reader_services); 503 initializePreBundledServicesMapFromArray(CATEGORY_AUDIO_AND_CAPTIONS, 504 R.array.config_preinstalled_audio_and_caption_services); 505 initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY, 506 R.array.config_preinstalled_display_services); 507 initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL, 508 R.array.config_preinstalled_interaction_control_services); 509 510 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity()); 511 512 List<AccessibilityServiceInfo> installedServices = 513 accessibilityManager.getInstalledAccessibilityServiceList(); 514 List<AccessibilityServiceInfo> enabledServiceInfos = accessibilityManager 515 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 516 Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( 517 getActivity()); 518 List<String> permittedServices = mDpm.getPermittedAccessibilityServices( 519 UserHandle.myUserId()); 520 521 PreferenceCategory downloadedServicesCategory = 522 mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES); 523 // Temporarily add the downloaded services category back if it was previously removed. 524 if (findPreference(CATEGORY_DOWNLOADED_SERVICES) == null) { 525 getPreferenceScreen().addPreference(downloadedServicesCategory); 526 } 527 528 for (int i = 0, count = installedServices.size(); i < count; ++i) { 529 final AccessibilityServiceInfo info = installedServices.get(i); 530 final ResolveInfo resolveInfo = info.getResolveInfo(); 531 532 final RestrictedPreference preference = 533 new RestrictedPreference(downloadedServicesCategory.getContext()); 534 final String title = resolveInfo.loadLabel(getPackageManager()).toString(); 535 536 Drawable icon; 537 if (resolveInfo.getIconResource() == 0) { 538 icon = ContextCompat.getDrawable(getContext(), R.mipmap.ic_accessibility_generic); 539 } else { 540 icon = resolveInfo.loadIcon(getPackageManager()); 541 } 542 543 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 544 final String packageName = serviceInfo.packageName; 545 final ComponentName componentName = new ComponentName(packageName, serviceInfo.name); 546 547 preference.setKey(componentName.flattenToString()); 548 549 preference.setTitle(title); 550 Utils.setSafeIcon(preference, icon); 551 final boolean serviceEnabled = enabledServices.contains(componentName); 552 String description = info.loadDescription(getPackageManager()); 553 if (TextUtils.isEmpty(description)) { 554 description = getString(R.string.accessibility_service_default_description); 555 } 556 557 if (serviceEnabled && AccessibilityUtils.hasServiceCrashed( 558 packageName, serviceInfo.name, enabledServiceInfos)) { 559 // Update the summaries for services that have crashed. 560 preference.setSummary(R.string.accessibility_summary_state_stopped); 561 description = getString(R.string.accessibility_description_state_stopped); 562 } else { 563 final CharSequence serviceSummary = getServiceSummary(getContext(), info, 564 serviceEnabled); 565 preference.setSummary(serviceSummary); 566 } 567 568 // Disable all accessibility services that are not permitted. 569 final boolean serviceAllowed = 570 permittedServices == null || permittedServices.contains(packageName); 571 if (!serviceAllowed && !serviceEnabled) { 572 final EnforcedAdmin admin = 573 RestrictedLockUtils.checkIfAccessibilityServiceDisallowed( 574 getActivity(), packageName, UserHandle.myUserId()); 575 if (admin != null) { 576 preference.setDisabledByAdmin(admin); 577 } else { 578 preference.setEnabled(false); 579 } 580 } else { 581 preference.setEnabled(true); 582 } 583 584 preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName()); 585 preference.setPersistent(true); 586 587 final Bundle extras = preference.getExtras(); 588 extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey()); 589 extras.putBoolean(EXTRA_CHECKED, serviceEnabled); 590 extras.putString(EXTRA_TITLE, title); 591 extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo); 592 extras.putString(EXTRA_SUMMARY, description); 593 594 final String settingsClassName = info.getSettingsActivityName(); 595 if (!TextUtils.isEmpty(settingsClassName)) { 596 extras.putString(EXTRA_SETTINGS_TITLE, 597 getString(R.string.accessibility_menu_item_settings)); 598 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME, 599 new ComponentName(packageName, settingsClassName).flattenToString()); 600 } 601 extras.putParcelable(EXTRA_COMPONENT_NAME, componentName); 602 603 PreferenceCategory prefCategory = downloadedServicesCategory; 604 // Set the appropriate category if the service comes pre-installed. 605 if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) { 606 prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName); 607 } 608 preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX); 609 prefCategory.addPreference(preference); 610 mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory); 611 } 612 613 // If the user has not installed any additional services, hide the category. 614 if (downloadedServicesCategory.getPreferenceCount() == 0) { 615 final PreferenceScreen screen = getPreferenceScreen(); 616 screen.removePreference(downloadedServicesCategory); 617 } 618 } 619 620 private void initializePreBundledServicesMapFromArray(String categoryKey, int key) { 621 String[] services = getResources().getStringArray(key); 622 PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey); 623 for (int i = 0; i < services.length; i++) { 624 ComponentName component = ComponentName.unflattenFromString(services[i]); 625 mPreBundledServiceComponentToCategoryMap.put(component, category); 626 } 627 } 628 629 protected void updateSystemPreferences() { 630 // Move color inversion and color correction preferences to Display category if device 631 // supports HWC hardware-accelerated color transform. 632 if (isColorTransformAccelerated(getContext())) { 633 PreferenceCategory experimentalCategory = 634 mCategoryToPrefCategoryMap.get(CATEGORY_EXPERIMENTAL); 635 PreferenceCategory displayCategory = 636 mCategoryToPrefCategoryMap.get(CATEGORY_DISPLAY); 637 experimentalCategory.removePreference(mToggleInversionPreference); 638 experimentalCategory.removePreference(mDisplayDaltonizerPreferenceScreen); 639 mToggleInversionPreference.setOrder(mToggleLargePointerIconPreference.getOrder()); 640 mDisplayDaltonizerPreferenceScreen.setOrder(mToggleInversionPreference.getOrder()); 641 mToggleInversionPreference.setSummary(R.string.summary_empty); 642 displayCategory.addPreference(mToggleInversionPreference); 643 displayCategory.addPreference(mDisplayDaltonizerPreferenceScreen); 644 } 645 646 // Text contrast. 647 mToggleHighTextContrastPreference.setChecked( 648 Settings.Secure.getInt(getContentResolver(), 649 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1); 650 651 // If the quick setting is enabled, the preference MUST be enabled. 652 mToggleInversionPreference.setChecked(Settings.Secure.getInt(getContentResolver(), 653 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0) == 1); 654 655 // Power button ends calls. 656 if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) 657 && Utils.isVoiceCapable(getActivity())) { 658 final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(), 659 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, 660 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT); 661 final boolean powerButtonEndsCall = 662 (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP); 663 mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall); 664 } 665 666 // Auto-rotate screen 667 updateLockScreenRotationCheckbox(); 668 669 // Large pointer icon. 670 mToggleLargePointerIconPreference.setChecked(Settings.Secure.getInt(getContentResolver(), 671 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0) != 0); 672 673 updateDisableAnimationsToggle(); 674 675 // Master mono 676 updateMasterMono(); 677 678 // Long press timeout. 679 final int longPressTimeout = Settings.Secure.getInt(getContentResolver(), 680 Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault); 681 String value = String.valueOf(longPressTimeout); 682 mSelectLongPressTimeoutPreference.setValue(value); 683 mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValueToTitleMap.get(value)); 684 685 updateVibrationSummary(mVibrationPreferenceScreen); 686 687 updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 688 mCaptioningPreferenceScreen); 689 updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 690 mDisplayDaltonizerPreferenceScreen); 691 692 updateMagnificationSummary(mDisplayMagnificationPreferenceScreen); 693 694 updateFontSizeSummary(mFontSizePreferenceScreen); 695 696 updateAutoclickSummary(mAutoclickPreferenceScreen); 697 698 updateAccessibilityShortcut(mAccessibilityShortcutPreferenceScreen); 699 } 700 701 private void updateMagnificationSummary(Preference pref) { 702 final boolean tripleTapEnabled = Settings.Secure.getInt(getContentResolver(), 703 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1; 704 final boolean buttonEnabled = Settings.Secure.getInt(getContentResolver(), 705 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1; 706 707 int summaryResId = 0; 708 if (!tripleTapEnabled && !buttonEnabled) { 709 summaryResId = R.string.accessibility_feature_state_off; 710 } else if (!tripleTapEnabled && buttonEnabled) { 711 summaryResId = R.string.accessibility_screen_magnification_navbar_title; 712 } else if (tripleTapEnabled && !buttonEnabled) { 713 summaryResId = R.string.accessibility_screen_magnification_gestures_title; 714 } else { 715 summaryResId = R.string.accessibility_screen_magnification_state_navbar_gesture; 716 } 717 pref.setSummary(summaryResId); 718 } 719 720 private void updateFeatureSummary(String prefKey, Preference pref) { 721 final boolean enabled = Settings.Secure.getInt(getContentResolver(), prefKey, 0) == 1; 722 pref.setSummary(enabled ? R.string.accessibility_feature_state_on 723 : R.string.accessibility_feature_state_off); 724 } 725 726 private void updateAutoclickSummary(Preference pref) { 727 final boolean enabled = Settings.Secure.getInt( 728 getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1; 729 if (!enabled) { 730 pref.setSummary(R.string.accessibility_feature_state_off); 731 return; 732 } 733 int delay = Settings.Secure.getInt( 734 getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, 735 AccessibilityManager.AUTOCLICK_DELAY_DEFAULT); 736 pref.setSummary(ToggleAutoclickPreferenceFragment.getAutoclickPreferenceSummary( 737 getResources(), delay)); 738 } 739 740 private void updateFontSizeSummary(Preference pref) { 741 final float currentScale = Settings.System.getFloat(getContext().getContentResolver(), 742 Settings.System.FONT_SCALE, 1.0f); 743 final Resources res = getContext().getResources(); 744 final String[] entries = res.getStringArray(R.array.entries_font_size); 745 final String[] strEntryValues = res.getStringArray(R.array.entryvalues_font_size); 746 final int index = ToggleFontSizePreferenceFragment.fontSizeValueToIndex(currentScale, 747 strEntryValues); 748 pref.setSummary(entries[index]); 749 } 750 751 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 752 void updateVibrationSummary(Preference pref) { 753 final Context context = getContext(); 754 final Vibrator vibrator = context.getSystemService(Vibrator.class); 755 756 final int ringIntensity = Settings.System.getInt(context.getContentResolver(), 757 Settings.System.NOTIFICATION_VIBRATION_INTENSITY, 758 vibrator.getDefaultNotificationVibrationIntensity()); 759 CharSequence ringIntensityString = 760 VibrationIntensityPreferenceController.getIntensityString(context, ringIntensity); 761 762 final int touchIntensity = Settings.System.getInt(context.getContentResolver(), 763 Settings.System.HAPTIC_FEEDBACK_INTENSITY, 764 vibrator.getDefaultHapticFeedbackIntensity()); 765 CharSequence touchIntensityString = 766 VibrationIntensityPreferenceController.getIntensityString(context, touchIntensity); 767 768 if (mVibrationPreferenceScreen == null) { 769 mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN); 770 } 771 772 if (ringIntensity == touchIntensity) { 773 mVibrationPreferenceScreen.setSummary(ringIntensityString); 774 } else { 775 mVibrationPreferenceScreen.setSummary( 776 getString(R.string.accessibility_vibration_summary, 777 ringIntensityString, touchIntensityString)); 778 } 779 } 780 781 private String getVibrationSummary(Context context, @VibrationIntensity int intensity) { 782 final boolean supportsMultipleIntensities = context.getResources().getBoolean( 783 R.bool.config_vibration_supports_multiple_intensities); 784 if (supportsMultipleIntensities) { 785 switch (intensity) { 786 case Vibrator.VIBRATION_INTENSITY_OFF: 787 return context.getString(R.string.accessibility_vibration_summary_off); 788 case Vibrator.VIBRATION_INTENSITY_LOW: 789 return context.getString(R.string.accessibility_vibration_summary_low); 790 case Vibrator.VIBRATION_INTENSITY_MEDIUM: 791 return context.getString(R.string.accessibility_vibration_summary_medium); 792 case Vibrator.VIBRATION_INTENSITY_HIGH: 793 return context.getString(R.string.accessibility_vibration_summary_high); 794 default: 795 return ""; 796 } 797 } else { 798 if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { 799 return context.getString(R.string.switch_on_text); 800 } else { 801 return context.getString(R.string.switch_off_text); 802 } 803 } 804 } 805 806 private void updateLockScreenRotationCheckbox() { 807 Context context = getActivity(); 808 if (context != null) { 809 mToggleLockScreenRotationPreference.setChecked( 810 !RotationPolicy.isRotationLocked(context)); 811 } 812 } 813 814 private void updateDisableAnimationsToggle() { 815 boolean allAnimationsDisabled = true; 816 for (String animationSetting : TOGGLE_ANIMATION_TARGETS) { 817 if (!TextUtils.equals( 818 Settings.Global.getString(getContentResolver(), animationSetting), 819 ANIMATION_OFF_VALUE)) { 820 allAnimationsDisabled = false; 821 break; 822 } 823 } 824 mToggleDisableAnimationsPreference.setChecked(allAnimationsDisabled); 825 } 826 827 private void updateMasterMono() { 828 final boolean masterMono = Settings.System.getIntForUser( 829 getContentResolver(), Settings.System.MASTER_MONO, 830 0 /* default */, UserHandle.USER_CURRENT) == 1; 831 mToggleMasterMonoPreference.setChecked(masterMono); 832 } 833 834 private void updateAccessibilityShortcut(Preference preference) { 835 if (AccessibilityManager.getInstance(getActivity()) 836 .getInstalledAccessibilityServiceList().isEmpty()) { 837 mAccessibilityShortcutPreferenceScreen 838 .setSummary(getString(R.string.accessibility_no_services_installed)); 839 mAccessibilityShortcutPreferenceScreen.setEnabled(false); 840 } else { 841 mAccessibilityShortcutPreferenceScreen.setEnabled(true); 842 boolean shortcutEnabled = 843 AccessibilityUtils.isShortcutEnabled(getContext(), UserHandle.myUserId()); 844 CharSequence summary = shortcutEnabled 845 ? AccessibilityShortcutPreferenceFragment.getServiceName(getContext()) 846 : getString(R.string.accessibility_feature_state_off); 847 mAccessibilityShortcutPreferenceScreen.setSummary(summary); 848 } 849 } 850 851 private static void configureMagnificationPreferenceIfNeeded(Preference preference) { 852 // Some devices support only a single magnification mode. In these cases, we redirect to 853 // the magnification mode's UI directly, rather than showing a PreferenceScreen with a 854 // single list item. 855 final Context context = preference.getContext(); 856 if (!MagnificationPreferenceFragment.isApplicable(context.getResources())) { 857 preference.setFragment(ToggleScreenMagnificationPreferenceFragment.class.getName()); 858 final Bundle extras = preference.getExtras(); 859 MagnificationGesturesPreferenceController 860 .populateMagnificationGesturesPreferenceExtras(extras, context); 861 } 862 } 863 864 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 865 new BaseSearchIndexProvider() { 866 867 public static final String KEY_DISPLAY_SIZE = "accessibility_settings_screen_zoom"; 868 869 @Override 870 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, 871 boolean enabled) { 872 List<SearchIndexableResource> indexables = new ArrayList<>(); 873 SearchIndexableResource indexable = new SearchIndexableResource(context); 874 indexable.xmlResId = R.xml.accessibility_settings; 875 indexables.add(indexable); 876 return indexables; 877 } 878 879 @Override 880 public List<String> getNonIndexableKeys(Context context) { 881 List<String> keys = super.getNonIndexableKeys(context); 882 // Duplicates in Display 883 keys.add(FONT_SIZE_PREFERENCE_SCREEN); 884 keys.add(KEY_DISPLAY_SIZE); 885 886 // Duplicates in Language & Input 887 keys.add(TTS_SETTINGS_PREFERENCE); 888 889 return keys; 890 } 891 }; 892 } 893