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 android.accessibilityservice.AccessibilityServiceInfo; 20 import android.app.ActivityManagerNative; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.content.ActivityNotFoundException; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.SharedPreferences; 29 import android.content.pm.ResolveInfo; 30 import android.content.pm.ServiceInfo; 31 import android.content.res.Configuration; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.RemoteException; 36 import android.os.SystemProperties; 37 import android.preference.CheckBoxPreference; 38 import android.preference.ListPreference; 39 import android.preference.Preference; 40 import android.preference.PreferenceCategory; 41 import android.preference.PreferenceScreen; 42 import android.provider.Settings; 43 import android.text.TextUtils; 44 import android.text.TextUtils.SimpleStringSplitter; 45 import android.util.Log; 46 import android.view.KeyCharacterMap; 47 import android.view.KeyEvent; 48 import android.view.View; 49 import android.view.accessibility.AccessibilityManager; 50 import android.widget.TextView; 51 52 import com.android.internal.content.PackageMonitor; 53 import com.android.internal.view.RotationPolicy; 54 import com.android.internal.view.RotationPolicy.RotationPolicyListener; 55 import com.android.settings.DialogCreatable; 56 import com.android.settings.R; 57 import com.android.settings.SettingsPreferenceFragment; 58 import com.android.settings.Utils; 59 60 import java.util.HashMap; 61 import java.util.HashSet; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.Set; 65 66 /** 67 * Activity with the accessibility settings. 68 */ 69 public class AccessibilitySettings extends SettingsPreferenceFragment implements DialogCreatable, 70 Preference.OnPreferenceChangeListener { 71 private static final String LOG_TAG = "AccessibilitySettings"; 72 73 private static final String DEFAULT_SCREENREADER_MARKET_LINK = 74 "market://search?q=pname:com.google.android.marvin.talkback"; 75 76 private static final float LARGE_FONT_SCALE = 1.3f; 77 78 private static final String SYSTEM_PROPERTY_MARKET_URL = "ro.screenreader.market"; 79 80 static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'; 81 82 private static final String KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE = 83 "key_install_accessibility_service_offered_once"; 84 85 // Preference categories 86 private static final String SERVICES_CATEGORY = "services_category"; 87 private static final String SYSTEM_CATEGORY = "system_category"; 88 89 // Preferences 90 private static final String TOGGLE_LARGE_TEXT_PREFERENCE = 91 "toggle_large_text_preference"; 92 private static final String TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE = 93 "toggle_power_button_ends_call_preference"; 94 private static final String TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE = 95 "toggle_lock_screen_rotation_preference"; 96 private static final String TOGGLE_SPEAK_PASSWORD_PREFERENCE = 97 "toggle_speak_password_preference"; 98 private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE = 99 "select_long_press_timeout_preference"; 100 private static final String ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN = 101 "enable_global_gesture_preference_screen"; 102 private static final String CAPTIONING_PREFERENCE_SCREEN = 103 "captioning_preference_screen"; 104 private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN = 105 "screen_magnification_preference_screen"; 106 107 // Extras passed to sub-fragments. 108 static final String EXTRA_PREFERENCE_KEY = "preference_key"; 109 static final String EXTRA_CHECKED = "checked"; 110 static final String EXTRA_TITLE = "title"; 111 static final String EXTRA_SUMMARY = "summary"; 112 static final String EXTRA_SETTINGS_TITLE = "settings_title"; 113 static final String EXTRA_COMPONENT_NAME = "component_name"; 114 static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name"; 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 // Dialog IDs. 123 private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 1; 124 125 // Auxiliary members. 126 final static SimpleStringSplitter sStringColonSplitter = 127 new SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 128 129 static final Set<ComponentName> sInstalledServices = new HashSet<ComponentName>(); 130 131 private final Map<String, String> mLongPressTimeoutValuetoTitleMap = 132 new HashMap<String, String>(); 133 134 private final Configuration mCurConfig = new Configuration(); 135 136 private final Handler mHandler = new Handler(); 137 138 private final Runnable mUpdateRunnable = new Runnable() { 139 @Override 140 public void run() { 141 loadInstalledServices(); 142 updateServicesPreferences(); 143 } 144 }; 145 146 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 147 @Override 148 public void onPackageAdded(String packageName, int uid) { 149 sendUpdate(); 150 } 151 152 @Override 153 public void onPackageAppeared(String packageName, int reason) { 154 sendUpdate(); 155 } 156 157 @Override 158 public void onPackageDisappeared(String packageName, int reason) { 159 sendUpdate(); 160 } 161 162 @Override 163 public void onPackageRemoved(String packageName, int uid) { 164 sendUpdate(); 165 } 166 167 private void sendUpdate() { 168 mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS); 169 } 170 }; 171 172 private final SettingsContentObserver mSettingsContentObserver = 173 new SettingsContentObserver(mHandler) { 174 @Override 175 public void onChange(boolean selfChange, Uri uri) { 176 loadInstalledServices(); 177 updateServicesPreferences(); 178 } 179 }; 180 181 private final RotationPolicyListener mRotationPolicyListener = new RotationPolicyListener() { 182 @Override 183 public void onChange() { 184 updateLockScreenRotationCheckbox(); 185 } 186 }; 187 188 // Preference controls. 189 private PreferenceCategory mServicesCategory; 190 private PreferenceCategory mSystemsCategory; 191 192 private CheckBoxPreference mToggleLargeTextPreference; 193 private CheckBoxPreference mTogglePowerButtonEndsCallPreference; 194 private CheckBoxPreference mToggleLockScreenRotationPreference; 195 private CheckBoxPreference mToggleSpeakPasswordPreference; 196 private ListPreference mSelectLongPressTimeoutPreference; 197 private Preference mNoServicesMessagePreference; 198 private PreferenceScreen mCaptioningPreferenceScreen; 199 private PreferenceScreen mDisplayMagnificationPreferenceScreen; 200 private PreferenceScreen mGlobalGesturePreferenceScreen; 201 202 private int mLongPressTimeoutDefault; 203 204 @Override 205 public void onCreate(Bundle icicle) { 206 super.onCreate(icicle); 207 addPreferencesFromResource(R.xml.accessibility_settings); 208 initializeAllPreferences(); 209 } 210 211 @Override 212 public void onResume() { 213 super.onResume(); 214 loadInstalledServices(); 215 updateAllPreferences(); 216 217 offerInstallAccessibilitySerivceOnce(); 218 219 mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false); 220 mSettingsContentObserver.register(getContentResolver()); 221 if (RotationPolicy.isRotationSupported(getActivity())) { 222 RotationPolicy.registerRotationPolicyListener(getActivity(), 223 mRotationPolicyListener); 224 } 225 } 226 227 @Override 228 public void onPause() { 229 mSettingsPackageMonitor.unregister(); 230 mSettingsContentObserver.unregister(getContentResolver()); 231 if (RotationPolicy.isRotationSupported(getActivity())) { 232 RotationPolicy.unregisterRotationPolicyListener(getActivity(), 233 mRotationPolicyListener); 234 } 235 super.onPause(); 236 } 237 238 @Override 239 public boolean onPreferenceChange(Preference preference, Object newValue) { 240 if (preference == mSelectLongPressTimeoutPreference) { 241 String stringValue = (String) newValue; 242 Settings.Secure.putInt(getContentResolver(), 243 Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue)); 244 mSelectLongPressTimeoutPreference.setSummary( 245 mLongPressTimeoutValuetoTitleMap.get(stringValue)); 246 return true; 247 } 248 return false; 249 } 250 251 @Override 252 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 253 if (mToggleLargeTextPreference == preference) { 254 handleToggleLargeTextPreferenceClick(); 255 return true; 256 } else if (mTogglePowerButtonEndsCallPreference == preference) { 257 handleTogglePowerButtonEndsCallPreferenceClick(); 258 return true; 259 } else if (mToggleLockScreenRotationPreference == preference) { 260 handleLockScreenRotationPreferenceClick(); 261 return true; 262 } else if (mToggleSpeakPasswordPreference == preference) { 263 handleToggleSpeakPasswordPreferenceClick(); 264 return true; 265 } else if (mGlobalGesturePreferenceScreen == preference) { 266 handleTogglEnableAccessibilityGesturePreferenceClick(); 267 return true; 268 } else if (mDisplayMagnificationPreferenceScreen == preference) { 269 handleDisplayMagnificationPreferenceScreenClick(); 270 return true; 271 } 272 return super.onPreferenceTreeClick(preferenceScreen, preference); 273 } 274 275 private void handleToggleLargeTextPreferenceClick() { 276 try { 277 mCurConfig.fontScale = mToggleLargeTextPreference.isChecked() ? LARGE_FONT_SCALE : 1; 278 ActivityManagerNative.getDefault().updatePersistentConfiguration(mCurConfig); 279 } catch (RemoteException re) { 280 /* ignore */ 281 } 282 } 283 284 private void handleTogglePowerButtonEndsCallPreferenceClick() { 285 Settings.Secure.putInt(getContentResolver(), 286 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, 287 (mTogglePowerButtonEndsCallPreference.isChecked() 288 ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP 289 : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF)); 290 } 291 292 private void handleLockScreenRotationPreferenceClick() { 293 RotationPolicy.setRotationLockForAccessibility(getActivity(), 294 !mToggleLockScreenRotationPreference.isChecked()); 295 } 296 297 private void handleToggleSpeakPasswordPreferenceClick() { 298 Settings.Secure.putInt(getContentResolver(), 299 Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 300 mToggleSpeakPasswordPreference.isChecked() ? 1 : 0); 301 } 302 303 private void handleTogglEnableAccessibilityGesturePreferenceClick() { 304 Bundle extras = mGlobalGesturePreferenceScreen.getExtras(); 305 extras.putString(EXTRA_TITLE, getString( 306 R.string.accessibility_global_gesture_preference_title)); 307 extras.putString(EXTRA_SUMMARY, getString( 308 R.string.accessibility_global_gesture_preference_description)); 309 extras.putBoolean(EXTRA_CHECKED, Settings.Global.getInt(getContentResolver(), 310 Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1); 311 super.onPreferenceTreeClick(mGlobalGesturePreferenceScreen, 312 mGlobalGesturePreferenceScreen); 313 } 314 315 private void handleDisplayMagnificationPreferenceScreenClick() { 316 Bundle extras = mDisplayMagnificationPreferenceScreen.getExtras(); 317 extras.putString(EXTRA_TITLE, getString( 318 R.string.accessibility_screen_magnification_title)); 319 extras.putCharSequence(EXTRA_SUMMARY, getActivity().getResources().getText( 320 R.string.accessibility_screen_magnification_summary)); 321 extras.putBoolean(EXTRA_CHECKED, Settings.Secure.getInt(getContentResolver(), 322 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1); 323 super.onPreferenceTreeClick(mDisplayMagnificationPreferenceScreen, 324 mDisplayMagnificationPreferenceScreen); 325 } 326 327 private void initializeAllPreferences() { 328 mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY); 329 mSystemsCategory = (PreferenceCategory) findPreference(SYSTEM_CATEGORY); 330 331 // Large text. 332 mToggleLargeTextPreference = 333 (CheckBoxPreference) findPreference(TOGGLE_LARGE_TEXT_PREFERENCE); 334 335 // Power button ends calls. 336 mTogglePowerButtonEndsCallPreference = 337 (CheckBoxPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE); 338 if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) 339 || !Utils.isVoiceCapable(getActivity())) { 340 mSystemsCategory.removePreference(mTogglePowerButtonEndsCallPreference); 341 } 342 343 // Lock screen rotation. 344 mToggleLockScreenRotationPreference = 345 (CheckBoxPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE); 346 if (!RotationPolicy.isRotationSupported(getActivity())) { 347 mSystemsCategory.removePreference(mToggleLockScreenRotationPreference); 348 } 349 350 // Speak passwords. 351 mToggleSpeakPasswordPreference = 352 (CheckBoxPreference) findPreference(TOGGLE_SPEAK_PASSWORD_PREFERENCE); 353 354 // Long press timeout. 355 mSelectLongPressTimeoutPreference = 356 (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE); 357 mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this); 358 if (mLongPressTimeoutValuetoTitleMap.size() == 0) { 359 String[] timeoutValues = getResources().getStringArray( 360 R.array.long_press_timeout_selector_values); 361 mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]); 362 String[] timeoutTitles = getResources().getStringArray( 363 R.array.long_press_timeout_selector_titles); 364 final int timeoutValueCount = timeoutValues.length; 365 for (int i = 0; i < timeoutValueCount; i++) { 366 mLongPressTimeoutValuetoTitleMap.put(timeoutValues[i], timeoutTitles[i]); 367 } 368 } 369 370 // Captioning. 371 mCaptioningPreferenceScreen = (PreferenceScreen) findPreference( 372 CAPTIONING_PREFERENCE_SCREEN); 373 374 // Display magnification. 375 mDisplayMagnificationPreferenceScreen = (PreferenceScreen) findPreference( 376 DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN); 377 378 // Global gesture. 379 mGlobalGesturePreferenceScreen = 380 (PreferenceScreen) findPreference(ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN); 381 final int longPressOnPowerBehavior = getActivity().getResources().getInteger( 382 com.android.internal.R.integer.config_longPressOnPowerBehavior); 383 final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; 384 if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) 385 || longPressOnPowerBehavior != LONG_PRESS_POWER_GLOBAL_ACTIONS) { 386 // Remove accessibility shortcut if power key is not present 387 // nor long press power does not show global actions menu. 388 mSystemsCategory.removePreference(mGlobalGesturePreferenceScreen); 389 } 390 } 391 392 private void updateAllPreferences() { 393 updateServicesPreferences(); 394 updateSystemPreferences(); 395 } 396 397 private void updateServicesPreferences() { 398 // Since services category is auto generated we have to do a pass 399 // to generate it since services can come and go and then based on 400 // the global accessibility state to decided whether it is enabled. 401 402 // Generate. 403 mServicesCategory.removeAll(); 404 405 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity()); 406 407 List<AccessibilityServiceInfo> installedServices = 408 accessibilityManager.getInstalledAccessibilityServiceList(); 409 Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( 410 getActivity()); 411 412 final boolean accessibilityEnabled = Settings.Secure.getInt(getContentResolver(), 413 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; 414 415 for (int i = 0, count = installedServices.size(); i < count; ++i) { 416 AccessibilityServiceInfo info = installedServices.get(i); 417 418 PreferenceScreen preference = getPreferenceManager().createPreferenceScreen( 419 getActivity()); 420 String title = info.getResolveInfo().loadLabel(getPackageManager()).toString(); 421 422 ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo; 423 ComponentName componentName = new ComponentName(serviceInfo.packageName, 424 serviceInfo.name); 425 426 preference.setKey(componentName.flattenToString()); 427 428 preference.setTitle(title); 429 final boolean serviceEnabled = accessibilityEnabled 430 && enabledServices.contains(componentName); 431 if (serviceEnabled) { 432 preference.setSummary(getString(R.string.accessibility_feature_state_on)); 433 } else { 434 preference.setSummary(getString(R.string.accessibility_feature_state_off)); 435 } 436 437 preference.setOrder(i); 438 preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName()); 439 preference.setPersistent(true); 440 441 Bundle extras = preference.getExtras(); 442 extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey()); 443 extras.putBoolean(EXTRA_CHECKED, serviceEnabled); 444 extras.putString(EXTRA_TITLE, title); 445 446 String description = info.loadDescription(getPackageManager()); 447 if (TextUtils.isEmpty(description)) { 448 description = getString(R.string.accessibility_service_default_description); 449 } 450 extras.putString(EXTRA_SUMMARY, description); 451 452 String settingsClassName = info.getSettingsActivityName(); 453 if (!TextUtils.isEmpty(settingsClassName)) { 454 extras.putString(EXTRA_SETTINGS_TITLE, 455 getString(R.string.accessibility_menu_item_settings)); 456 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME, 457 new ComponentName(info.getResolveInfo().serviceInfo.packageName, 458 settingsClassName).flattenToString()); 459 } 460 461 extras.putParcelable(EXTRA_COMPONENT_NAME, componentName); 462 463 mServicesCategory.addPreference(preference); 464 } 465 466 if (mServicesCategory.getPreferenceCount() == 0) { 467 if (mNoServicesMessagePreference == null) { 468 mNoServicesMessagePreference = new Preference(getActivity()) { 469 @Override 470 protected void onBindView(View view) { 471 super.onBindView(view); 472 TextView summaryView = (TextView) view.findViewById(R.id.summary); 473 String title = getString(R.string.accessibility_no_services_installed); 474 summaryView.setText(title); 475 } 476 }; 477 mNoServicesMessagePreference.setPersistent(false); 478 mNoServicesMessagePreference.setLayoutResource( 479 R.layout.text_description_preference); 480 mNoServicesMessagePreference.setSelectable(false); 481 } 482 mServicesCategory.addPreference(mNoServicesMessagePreference); 483 } 484 } 485 486 private void updateSystemPreferences() { 487 // Large text. 488 try { 489 mCurConfig.updateFrom(ActivityManagerNative.getDefault().getConfiguration()); 490 } catch (RemoteException re) { 491 /* ignore */ 492 } 493 mToggleLargeTextPreference.setChecked(mCurConfig.fontScale == LARGE_FONT_SCALE); 494 495 // Power button ends calls. 496 if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER) 497 && Utils.isVoiceCapable(getActivity())) { 498 final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(), 499 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, 500 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT); 501 final boolean powerButtonEndsCall = 502 (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP); 503 mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall); 504 } 505 506 // Auto-rotate screen 507 updateLockScreenRotationCheckbox(); 508 509 // Speak passwords. 510 final boolean speakPasswordEnabled = Settings.Secure.getInt(getContentResolver(), 511 Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0; 512 mToggleSpeakPasswordPreference.setChecked(speakPasswordEnabled); 513 514 // Long press timeout. 515 final int longPressTimeout = Settings.Secure.getInt(getContentResolver(), 516 Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault); 517 String value = String.valueOf(longPressTimeout); 518 mSelectLongPressTimeoutPreference.setValue(value); 519 mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value)); 520 521 // Captioning. 522 final boolean captioningEnabled = Settings.Secure.getInt(getContentResolver(), 523 Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0) == 1; 524 if (captioningEnabled) { 525 mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_on); 526 } else { 527 mCaptioningPreferenceScreen.setSummary(R.string.accessibility_feature_state_off); 528 } 529 530 // Screen magnification. 531 final boolean magnificationEnabled = Settings.Secure.getInt(getContentResolver(), 532 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1; 533 if (magnificationEnabled) { 534 mDisplayMagnificationPreferenceScreen.setSummary( 535 R.string.accessibility_feature_state_on); 536 } else { 537 mDisplayMagnificationPreferenceScreen.setSummary( 538 R.string.accessibility_feature_state_off); 539 } 540 541 // Global gesture 542 final boolean globalGestureEnabled = Settings.Global.getInt(getContentResolver(), 543 Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1; 544 if (globalGestureEnabled) { 545 mGlobalGesturePreferenceScreen.setSummary( 546 R.string.accessibility_global_gesture_preference_summary_on); 547 } else { 548 mGlobalGesturePreferenceScreen.setSummary( 549 R.string.accessibility_global_gesture_preference_summary_off); 550 } 551 } 552 553 private void updateLockScreenRotationCheckbox() { 554 Context context = getActivity(); 555 if (context != null) { 556 mToggleLockScreenRotationPreference.setChecked( 557 !RotationPolicy.isRotationLocked(context)); 558 } 559 } 560 561 private void offerInstallAccessibilitySerivceOnce() { 562 // There is always one preference - if no services it is just a message. 563 if (mServicesCategory.getPreference(0) != mNoServicesMessagePreference) { 564 return; 565 } 566 SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE); 567 final boolean offerInstallService = !preferences.getBoolean( 568 KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE, false); 569 if (offerInstallService) { 570 String screenreaderMarketLink = SystemProperties.get( 571 SYSTEM_PROPERTY_MARKET_URL, 572 DEFAULT_SCREENREADER_MARKET_LINK); 573 Uri marketUri = Uri.parse(screenreaderMarketLink); 574 Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri); 575 576 if (getPackageManager().resolveActivity(marketIntent, 0) == null) { 577 // Don't show the dialog if no market app is found/installed. 578 return; 579 } 580 581 preferences.edit().putBoolean(KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE, 582 true).commit(); 583 // Notify user that they do not have any accessibility 584 // services installed and direct them to Market to get TalkBack. 585 showDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES); 586 } 587 } 588 589 @Override 590 public Dialog onCreateDialog(int dialogId) { 591 switch (dialogId) { 592 case DIALOG_ID_NO_ACCESSIBILITY_SERVICES: 593 return new AlertDialog.Builder(getActivity()) 594 .setTitle(R.string.accessibility_service_no_apps_title) 595 .setMessage(R.string.accessibility_service_no_apps_message) 596 .setPositiveButton(android.R.string.ok, 597 new DialogInterface.OnClickListener() { 598 @Override 599 public void onClick(DialogInterface dialog, int which) { 600 // dismiss the dialog before launching 601 // the activity otherwise the dialog 602 // removal occurs after 603 // onSaveInstanceState which triggers an 604 // exception 605 removeDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES); 606 String screenreaderMarketLink = SystemProperties.get( 607 SYSTEM_PROPERTY_MARKET_URL, 608 DEFAULT_SCREENREADER_MARKET_LINK); 609 Uri marketUri = Uri.parse(screenreaderMarketLink); 610 Intent marketIntent = new Intent(Intent.ACTION_VIEW, 611 marketUri); 612 try { 613 startActivity(marketIntent); 614 } catch (ActivityNotFoundException anfe) { 615 Log.w(LOG_TAG, "Couldn't start play store activity", 616 anfe); 617 } 618 } 619 }) 620 .setNegativeButton(android.R.string.cancel, null) 621 .create(); 622 default: 623 return null; 624 } 625 } 626 627 private void loadInstalledServices() { 628 Set<ComponentName> installedServices = sInstalledServices; 629 installedServices.clear(); 630 631 List<AccessibilityServiceInfo> installedServiceInfos = 632 AccessibilityManager.getInstance(getActivity()) 633 .getInstalledAccessibilityServiceList(); 634 if (installedServiceInfos == null) { 635 return; 636 } 637 638 final int installedServiceInfoCount = installedServiceInfos.size(); 639 for (int i = 0; i < installedServiceInfoCount; i++) { 640 ResolveInfo resolveInfo = installedServiceInfos.get(i).getResolveInfo(); 641 ComponentName installedService = new ComponentName( 642 resolveInfo.serviceInfo.packageName, 643 resolveInfo.serviceInfo.name); 644 installedServices.add(installedService); 645 } 646 } 647 } 648