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