InputMethodAndLanguageSettings.java revision cd6fdead7c7a42f4a18a37523bb4feef230559c0
1/* 2 * Copyright (C) 2008 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 17package com.android.settings.inputmethod; 18 19import android.app.Activity; 20import android.app.Fragment; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.pm.PackageManager; 26import android.content.pm.ResolveInfo; 27import android.content.pm.ServiceInfo; 28import android.content.res.Configuration; 29import android.content.res.Resources; 30import android.database.ContentObserver; 31import android.hardware.input.InputDeviceIdentifier; 32import android.hardware.input.InputManager; 33import android.hardware.input.KeyboardLayout; 34import android.os.Bundle; 35import android.os.Handler; 36import android.preference.CheckBoxPreference; 37import android.preference.ListPreference; 38import android.preference.Preference; 39import android.preference.Preference.OnPreferenceClickListener; 40import android.preference.PreferenceCategory; 41import android.preference.PreferenceScreen; 42import android.provider.Settings; 43import android.provider.Settings.System; 44import android.speech.RecognitionService; 45import android.speech.tts.TtsEngines; 46import android.text.TextUtils; 47import android.view.InputDevice; 48import android.view.inputmethod.InputMethodInfo; 49import android.view.inputmethod.InputMethodManager; 50import android.view.inputmethod.InputMethodSubtype; 51import android.view.textservice.SpellCheckerInfo; 52import android.view.textservice.TextServicesManager; 53 54import com.android.settings.R; 55import com.android.settings.Settings.KeyboardLayoutPickerActivity; 56import com.android.settings.SettingsActivity; 57import com.android.settings.SettingsPreferenceFragment; 58import com.android.settings.SubSettings; 59import com.android.settings.UserDictionarySettings; 60import com.android.settings.Utils; 61import com.android.settings.VoiceInputOutputSettings; 62import com.android.settings.search.BaseSearchIndexProvider; 63import com.android.settings.search.Indexable; 64import com.android.settings.search.SearchIndexableRaw; 65 66import java.text.Collator; 67import java.util.ArrayList; 68import java.util.Collections; 69import java.util.Comparator; 70import java.util.List; 71import java.util.TreeSet; 72 73public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment 74 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener, 75 KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable, 76 InputMethodPreference.OnSavePreferenceListener { 77 private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings"; 78 private static final String KEY_PHONE_LANGUAGE = "phone_language"; 79 private static final String KEY_CHOOSE_INPUT_METHODS = "choose_input_methods"; 80 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method"; 81 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector"; 82 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings"; 83 // false: on ICS or later 84 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false; 85 86 private static final String[] sSystemSettingNames = { 87 System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, 88 }; 89 90 private static final String[] sHardKeyboardKeys = { 91 "auto_replace", "auto_caps", "auto_punctuate", 92 }; 93 94 private int mDefaultInputMethodSelectorVisibility = 0; 95 private ListPreference mShowInputMethodSelectorPref; 96 private PreferenceCategory mKeyboardSettingsCategory; 97 private PreferenceCategory mHardKeyboardCategory; 98 private PreferenceCategory mGameControllerCategory; 99 private Preference mLanguagePref; 100 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>(); 101 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>(); 102 private InputManager mIm; 103 private InputMethodManager mImm; 104 private boolean mShowsOnlyFullImeAndKeyboardList; 105 private Handler mHandler; 106 private SettingsObserver mSettingsObserver; 107 private Intent mIntentWaitingForResult; 108 private InputMethodSettingValuesWrapper mInputMethodSettingValues; 109 110 @Override 111 public void onCreate(Bundle icicle) { 112 super.onCreate(icicle); 113 114 addPreferencesFromResource(R.xml.language_settings); 115 116 final Activity activity = getActivity(); 117 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 118 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity); 119 120 try { 121 mDefaultInputMethodSelectorVisibility = Integer.valueOf( 122 getString(R.string.input_method_selector_visibility_default_value)); 123 } catch (NumberFormatException e) { 124 } 125 126 if (activity.getAssets().getLocales().length == 1) { 127 // No "Select language" pref if there's only one system locale available. 128 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE)); 129 } else { 130 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE); 131 } 132 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 133 mShowInputMethodSelectorPref = (ListPreference)findPreference( 134 KEY_INPUT_METHOD_SELECTOR); 135 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); 136 // TODO: Update current input method name on summary 137 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility()); 138 } 139 140 new VoiceInputOutputSettings(this).onCreate(); 141 142 // Get references to dynamically constructed categories. 143 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard"); 144 mKeyboardSettingsCategory = (PreferenceCategory)findPreference( 145 "keyboard_settings_category"); 146 mGameControllerCategory = (PreferenceCategory)findPreference( 147 "game_controller_settings_category"); 148 149 // Filter out irrelevant features if invoked from IME settings button. 150 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( 151 activity.getIntent().getAction()); 152 activity.getIntent().setAction(null); 153 if (mShowsOnlyFullImeAndKeyboardList) { 154 getPreferenceScreen().removeAll(); 155 getPreferenceScreen().addPreference(mHardKeyboardCategory); 156 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 157 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); 158 } 159 mKeyboardSettingsCategory.removeAll(); 160 getPreferenceScreen().addPreference(mKeyboardSettingsCategory); 161 } else { 162 final Preference pref = findPreference(KEY_CHOOSE_INPUT_METHODS); 163 final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS); 164 intent.setClass(activity, SubSettings.class); 165 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, getClass().getName()); 166 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 167 R.string.choose_input_methods); 168 pref.setIntent(intent); 169 } 170 171 // Build hard keyboard and game controller preference categories. 172 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE); 173 updateInputDevices(); 174 175 // Spell Checker 176 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS); 177 if (spellChecker != null) { 178 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML. 179 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker); 180 final Intent intent = new Intent(Intent.ACTION_MAIN); 181 intent.setClass(activity, SubSettings.class); 182 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, 183 SpellCheckersSettings.class.getName()); 184 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 185 R.string.spellcheckers_settings_title); 186 spellChecker.setIntent(intent); 187 } 188 189 mHandler = new Handler(); 190 mSettingsObserver = new SettingsObserver(mHandler, activity); 191 } 192 193 private void updateInputMethodSelectorSummary(int value) { 194 String[] inputMethodSelectorTitles = getResources().getStringArray( 195 R.array.input_method_selector_titles); 196 if (inputMethodSelectorTitles.length > value) { 197 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]); 198 mShowInputMethodSelectorPref.setValue(String.valueOf(value)); 199 } 200 } 201 202 private void updateUserDictionaryPreference(Preference userDictionaryPreference) { 203 final Activity activity = getActivity(); 204 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity); 205 if (null == localeSet) { 206 // The locale list is null if and only if the user dictionary service is 207 // not present or disabled. In this case we need to remove the preference. 208 getPreferenceScreen().removePreference(userDictionaryPreference); 209 } else { 210 userDictionaryPreference.setOnPreferenceClickListener( 211 new OnPreferenceClickListener() { 212 @Override 213 public boolean onPreferenceClick(Preference arg0) { 214 // Redirect to UserDictionarySettings if the user needs only one 215 // language. 216 final Bundle extras = new Bundle(); 217 final Class<? extends Fragment> targetFragment; 218 if (localeSet.size() <= 1) { 219 if (!localeSet.isEmpty()) { 220 // If the size of localeList is 0, we don't set the locale 221 // parameter in the extras. This will be interpreted by the 222 // UserDictionarySettings class as meaning 223 // "the current locale". Note that with the current code for 224 // UserDictionaryList#getUserDictionaryLocalesSet() 225 // the locale list always has at least one element, since it 226 // always includes the current locale explicitly. 227 // @see UserDictionaryList.getUserDictionaryLocalesSet(). 228 extras.putString("locale", localeSet.first()); 229 } 230 targetFragment = UserDictionarySettings.class; 231 } else { 232 targetFragment = UserDictionaryList.class; 233 } 234 startFragment(InputMethodAndLanguageSettings.this, 235 targetFragment.getCanonicalName(), -1, -1, extras); 236 return true; 237 } 238 }); 239 } 240 } 241 242 @Override 243 public void onResume() { 244 super.onResume(); 245 246 mSettingsObserver.resume(); 247 mIm.registerInputDeviceListener(this, null); 248 249 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS); 250 if (spellChecker != null) { 251 final TextServicesManager tsm = (TextServicesManager) getSystemService( 252 Context.TEXT_SERVICES_MANAGER_SERVICE); 253 if (tsm.isSpellCheckerEnabled()) { 254 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker(); 255 spellChecker.setSummary(sci.loadLabel(getPackageManager())); 256 } else { 257 spellChecker.setSummary(R.string.switch_off_text); 258 } 259 } 260 261 if (!mShowsOnlyFullImeAndKeyboardList) { 262 if (mLanguagePref != null) { 263 String localeName = getLocaleName(getResources()); 264 mLanguagePref.setSummary(localeName); 265 } 266 267 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); 268 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 269 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); 270 } 271 } 272 273 // Hard keyboard 274 if (!mHardKeyboardPreferenceList.isEmpty()) { 275 for (int i = 0; i < sHardKeyboardKeys.length; ++i) { 276 CheckBoxPreference chkPref = (CheckBoxPreference) 277 mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); 278 chkPref.setChecked( 279 System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); 280 } 281 } 282 283 updateInputDevices(); 284 285 // Refresh internal states in mInputMethodSettingValues to keep the latest 286 // "InputMethodInfo"s and "InputMethodSubtype"s 287 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); 288 updateInputMethodPreferenceViews(); 289 } 290 291 @Override 292 public void onPause() { 293 super.onPause(); 294 295 mIm.unregisterInputDeviceListener(this); 296 mSettingsObserver.pause(); 297 298 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 299 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); 300 } 301 // TODO: Consolidate the logic to InputMethodSettingsWrapper 302 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( 303 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(), 304 !mHardKeyboardPreferenceList.isEmpty()); 305 } 306 307 @Override 308 public void onInputDeviceAdded(int deviceId) { 309 updateInputDevices(); 310 } 311 312 @Override 313 public void onInputDeviceChanged(int deviceId) { 314 updateInputDevices(); 315 } 316 317 @Override 318 public void onInputDeviceRemoved(int deviceId) { 319 updateInputDevices(); 320 } 321 322 @Override 323 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 324 // Input Method stuff 325 if (Utils.isMonkeyRunning()) { 326 return false; 327 } 328 if (preference instanceof PreferenceScreen) { 329 if (preference.getFragment() != null) { 330 // Fragment will be handled correctly by the super class. 331 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) { 332 final InputMethodManager imm = (InputMethodManager) 333 getSystemService(Context.INPUT_METHOD_SERVICE); 334 imm.showInputMethodPicker(); 335 } 336 } else if (preference instanceof CheckBoxPreference) { 337 final CheckBoxPreference chkPref = (CheckBoxPreference) preference; 338 if (!mHardKeyboardPreferenceList.isEmpty()) { 339 for (int i = 0; i < sHardKeyboardKeys.length; ++i) { 340 if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { 341 System.putInt(getContentResolver(), sSystemSettingNames[i], 342 chkPref.isChecked() ? 1 : 0); 343 return true; 344 } 345 } 346 } 347 if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) { 348 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES, 349 chkPref.isChecked() ? 1 : 0); 350 return true; 351 } 352 } 353 return super.onPreferenceTreeClick(preferenceScreen, preference); 354 } 355 356 private static String getLocaleName(Resources resources) { 357 Configuration conf = resources.getConfiguration(); 358 String language = conf.locale.getLanguage(); 359 String localeName; 360 // TODO: This is not an accurate way to display the locale, as it is 361 // just working around the fact that we support limited dialects 362 // and want to pretend that the language is valid for all locales. 363 // We need a way to support languages that aren't tied to a particular 364 // locale instead of hiding the locale qualifier. 365 if (language.equals("zz")) { 366 String country = conf.locale.getCountry(); 367 if (country.equals("ZZ")) { 368 localeName = "[Developer] Accented English (zz_ZZ)"; 369 } else if (country.equals("ZY")) { 370 localeName = "[Developer] Fake Bi-Directional (zz_ZY)"; 371 } else { 372 localeName = ""; 373 } 374 } else if (hasOnlyOneLanguageInstance(language, 375 Resources.getSystem().getAssets().getLocales())) { 376 localeName = conf.locale.getDisplayLanguage(conf.locale); 377 } else { 378 localeName = conf.locale.getDisplayName(conf.locale); 379 } 380 381 if (localeName.length() > 1) { 382 localeName = Character.toUpperCase(localeName.charAt(0)) 383 + localeName.substring(1); 384 } 385 386 return localeName; 387 } 388 389 private static boolean hasOnlyOneLanguageInstance(String languageCode, String[] locales) { 390 int count = 0; 391 for (String localeCode : locales) { 392 if (localeCode.length() > 2 393 && localeCode.startsWith(languageCode)) { 394 count++; 395 if (count > 1) { 396 return false; 397 } 398 } 399 } 400 return count == 1; 401 } 402 403 private void saveInputMethodSelectorVisibility(String value) { 404 try { 405 int intValue = Integer.valueOf(value); 406 Settings.Secure.putInt(getContentResolver(), 407 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue); 408 updateInputMethodSelectorSummary(intValue); 409 } catch(NumberFormatException e) { 410 } 411 } 412 413 private int loadInputMethodSelectorVisibility() { 414 return Settings.Secure.getInt(getContentResolver(), 415 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, 416 mDefaultInputMethodSelectorVisibility); 417 } 418 419 @Override 420 public boolean onPreferenceChange(Preference preference, Object value) { 421 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 422 if (preference == mShowInputMethodSelectorPref) { 423 if (value instanceof String) { 424 saveInputMethodSelectorVisibility((String)value); 425 } 426 } 427 } 428 return false; 429 } 430 431 private void updateInputMethodPreferenceViews() { 432 synchronized (mInputMethodPreferenceList) { 433 // Clear existing "InputMethodPreference"s 434 for (final InputMethodPreference pref : mInputMethodPreferenceList) { 435 mKeyboardSettingsCategory.removePreference(pref); 436 } 437 mInputMethodPreferenceList.clear(); 438 final Context context = getActivity(); 439 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList 440 ? mInputMethodSettingValues.getInputMethodList() 441 : mImm.getEnabledInputMethodList(); 442 final int N = (imis == null ? 0 : imis.size()); 443 for (int i = 0; i < N; ++i) { 444 final InputMethodInfo imi = imis.get(i); 445 final InputMethodPreference pref = new InputMethodPreference( 446 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */, this); 447 mInputMethodPreferenceList.add(pref); 448 } 449 final Collator collator = Collator.getInstance(); 450 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() { 451 @Override 452 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) { 453 return lhs.compareTo(rhs, collator); 454 } 455 }); 456 for (int i = 0; i < N; ++i) { 457 final InputMethodPreference pref = mInputMethodPreferenceList.get(i); 458 mKeyboardSettingsCategory.addPreference(pref); 459 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); 460 pref.updatePreferenceViews(); 461 } 462 } 463 updateCurrentImeName(); 464 // TODO: Consolidate the logic with InputMethodSettingsWrapper 465 // CAVEAT: The preference class here does not know about the default value - that is 466 // managed by the Input Method Manager Service, so in this case it could save the wrong 467 // value. Hence we must update the checkboxes here. 468 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( 469 this, getContentResolver(), 470 mInputMethodSettingValues.getInputMethodList(), null); 471 } 472 473 @Override 474 public void onSaveInputMethodPreference(final InputMethodPreference pref) { 475 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard 476 == Configuration.KEYBOARD_QWERTY; 477 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), 478 mImm.getInputMethodList(), hasHardwareKeyboard); 479 // Update input method settings and preference list. 480 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); 481 for (final InputMethodPreference p : mInputMethodPreferenceList) { 482 p.updatePreferenceViews(); 483 } 484 } 485 486 private void updateCurrentImeName() { 487 final Context context = getActivity(); 488 if (context == null || mImm == null) return; 489 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD); 490 if (curPref != null) { 491 final CharSequence curIme = 492 mInputMethodSettingValues.getCurrentInputMethodName(context); 493 if (!TextUtils.isEmpty(curIme)) { 494 synchronized (this) { 495 curPref.setSummary(curIme); 496 } 497 } 498 } 499 } 500 501 private void updateInputDevices() { 502 updateHardKeyboards(); 503 updateGameControllers(); 504 } 505 506 private void updateHardKeyboards() { 507 mHardKeyboardPreferenceList.clear(); 508 if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { 509 final int[] devices = InputDevice.getDeviceIds(); 510 for (int i = 0; i < devices.length; i++) { 511 InputDevice device = InputDevice.getDevice(devices[i]); 512 if (device != null 513 && !device.isVirtual() 514 && device.isFullKeyboard()) { 515 final InputDeviceIdentifier identifier = device.getIdentifier(); 516 final String keyboardLayoutDescriptor = 517 mIm.getCurrentKeyboardLayoutForInputDevice(identifier); 518 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? 519 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null; 520 521 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null); 522 pref.setTitle(device.getName()); 523 if (keyboardLayout != null) { 524 pref.setSummary(keyboardLayout.toString()); 525 } else { 526 pref.setSummary(R.string.keyboard_layout_default_label); 527 } 528 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 529 @Override 530 public boolean onPreferenceClick(Preference preference) { 531 showKeyboardLayoutDialog(identifier); 532 return true; 533 } 534 }); 535 mHardKeyboardPreferenceList.add(pref); 536 } 537 } 538 } 539 540 if (!mHardKeyboardPreferenceList.isEmpty()) { 541 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) { 542 final Preference pref = mHardKeyboardCategory.getPreference(i); 543 if (pref.getOrder() < 1000) { 544 mHardKeyboardCategory.removePreference(pref); 545 } 546 } 547 548 Collections.sort(mHardKeyboardPreferenceList); 549 final int count = mHardKeyboardPreferenceList.size(); 550 for (int i = 0; i < count; i++) { 551 final Preference pref = mHardKeyboardPreferenceList.get(i); 552 pref.setOrder(i); 553 mHardKeyboardCategory.addPreference(pref); 554 } 555 556 getPreferenceScreen().addPreference(mHardKeyboardCategory); 557 } else { 558 getPreferenceScreen().removePreference(mHardKeyboardCategory); 559 } 560 } 561 562 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) { 563 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment( 564 inputDeviceIdentifier); 565 fragment.setTargetFragment(this, 0); 566 fragment.show(getActivity().getFragmentManager(), "keyboardLayout"); 567 } 568 569 @Override 570 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) { 571 final Intent intent = new Intent(Intent.ACTION_MAIN); 572 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class); 573 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER, 574 inputDeviceIdentifier); 575 mIntentWaitingForResult = intent; 576 startActivityForResult(intent, 0); 577 } 578 579 @Override 580 public void onActivityResult(int requestCode, int resultCode, Intent data) { 581 super.onActivityResult(requestCode, resultCode, data); 582 583 if (mIntentWaitingForResult != null) { 584 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult 585 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER); 586 mIntentWaitingForResult = null; 587 showKeyboardLayoutDialog(inputDeviceIdentifier); 588 } 589 } 590 591 private void updateGameControllers() { 592 if (haveInputDeviceWithVibrator()) { 593 getPreferenceScreen().addPreference(mGameControllerCategory); 594 595 CheckBoxPreference chkPref = (CheckBoxPreference) 596 mGameControllerCategory.findPreference("vibrate_input_devices"); 597 chkPref.setChecked(System.getInt(getContentResolver(), 598 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0); 599 } else { 600 getPreferenceScreen().removePreference(mGameControllerCategory); 601 } 602 } 603 604 private static boolean haveInputDeviceWithVibrator() { 605 final int[] devices = InputDevice.getDeviceIds(); 606 for (int i = 0; i < devices.length; i++) { 607 InputDevice device = InputDevice.getDevice(devices[i]); 608 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) { 609 return true; 610 } 611 } 612 return false; 613 } 614 615 private class SettingsObserver extends ContentObserver { 616 private Context mContext; 617 618 public SettingsObserver(Handler handler, Context context) { 619 super(handler); 620 mContext = context; 621 } 622 623 @Override public void onChange(boolean selfChange) { 624 updateCurrentImeName(); 625 } 626 627 public void resume() { 628 final ContentResolver cr = mContext.getContentResolver(); 629 cr.registerContentObserver( 630 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this); 631 cr.registerContentObserver(Settings.Secure.getUriFor( 632 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); 633 } 634 635 public void pause() { 636 mContext.getContentResolver().unregisterContentObserver(this); 637 } 638 } 639 640 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 641 new BaseSearchIndexProvider() { 642 @Override 643 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 644 List<SearchIndexableRaw> indexables = new ArrayList<>(); 645 646 Resources resources = context.getResources(); 647 String screenTitle = context.getString(R.string.language_keyboard_settings_title); 648 649 // Locale picker. 650 if (context.getAssets().getLocales().length > 1) { 651 String localeName = getLocaleName(resources); 652 SearchIndexableRaw indexable = new SearchIndexableRaw(context); 653 indexable.key = KEY_PHONE_LANGUAGE; 654 indexable.title = context.getString(R.string.phone_language); 655 indexable.summaryOn = localeName; 656 indexable.summaryOff = localeName; 657 indexable.screenTitle = screenTitle; 658 indexables.add(indexable); 659 } 660 661 // Spell checker. 662 SearchIndexableRaw indexable = new SearchIndexableRaw(context); 663 indexable.key = KEY_SPELL_CHECKERS; 664 indexable.title = context.getString(R.string.spellcheckers_settings_title); 665 indexable.screenTitle = screenTitle; 666 indexables.add(indexable); 667 668 // User dictionary. 669 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) { 670 indexable = new SearchIndexableRaw(context); 671 indexable.key = "user_dict_settings"; 672 indexable.title = context.getString(R.string.user_dict_settings_title); 673 indexable.screenTitle = screenTitle; 674 indexables.add(indexable); 675 } 676 677 // Keyboard settings. 678 indexable = new SearchIndexableRaw(context); 679 indexable.key = "keyboard_settings"; 680 indexable.title = context.getString(R.string.keyboard_settings_category); 681 indexable.screenTitle = screenTitle; 682 indexables.add(indexable); 683 684 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper 685 .getInstance(context); 686 immValues.refreshAllInputMethodAndSubtypes(); 687 688 // Current IME. 689 String currImeName = immValues.getCurrentInputMethodName(context).toString(); 690 indexable = new SearchIndexableRaw(context); 691 indexable.key = KEY_CURRENT_INPUT_METHOD; 692 indexable.title = context.getString(R.string.current_input_method); 693 indexable.summaryOn = currImeName; 694 indexable.summaryOff = currImeName; 695 indexable.screenTitle = screenTitle; 696 indexables.add(indexable); 697 698 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService( 699 Context.INPUT_METHOD_SERVICE); 700 701 // All other IMEs. 702 List<InputMethodInfo> inputMethods = immValues.getInputMethodList(); 703 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size()); 704 for (int i = 0; i < inputMethodCount; ++i) { 705 InputMethodInfo inputMethod = inputMethods.get(i); 706 707 StringBuilder builder = new StringBuilder(); 708 List<InputMethodSubtype> subtypes = inputMethodManager 709 .getEnabledInputMethodSubtypeList(inputMethod, true); 710 final int subtypeCount = subtypes.size(); 711 for (int j = 0; j < subtypeCount; j++) { 712 InputMethodSubtype subtype = subtypes.get(j); 713 if (builder.length() > 0) { 714 builder.append(','); 715 } 716 CharSequence subtypeLabel = subtype.getDisplayName(context, 717 inputMethod.getPackageName(), inputMethod.getServiceInfo() 718 .applicationInfo); 719 builder.append(subtypeLabel); 720 } 721 String summary = builder.toString(); 722 723 ServiceInfo serviceInfo = inputMethod.getServiceInfo(); 724 ComponentName componentName = new ComponentName(serviceInfo.packageName, 725 serviceInfo.name); 726 727 indexable = new SearchIndexableRaw(context); 728 indexable.key = componentName.flattenToString(); 729 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString(); 730 indexable.summaryOn = summary; 731 indexable.summaryOff = summary; 732 indexable.screenTitle = screenTitle; 733 indexables.add(indexable); 734 } 735 736 // Hard keyboards 737 InputManager inputManager = (InputManager) context.getSystemService( 738 Context.INPUT_SERVICE); 739 if (resources.getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { 740 boolean hasHardKeyboards = false; 741 742 final int[] devices = InputDevice.getDeviceIds(); 743 for (int i = 0; i < devices.length; i++) { 744 InputDevice device = InputDevice.getDevice(devices[i]); 745 if (device == null || device.isVirtual() || !device.isFullKeyboard()) { 746 continue; 747 } 748 749 hasHardKeyboards = true; 750 751 InputDeviceIdentifier identifier = device.getIdentifier(); 752 String keyboardLayoutDescriptor = 753 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier); 754 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? 755 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null; 756 757 String summary; 758 if (keyboardLayout != null) { 759 summary = keyboardLayout.toString(); 760 } else { 761 summary = context.getString(R.string.keyboard_layout_default_label); 762 } 763 764 indexable = new SearchIndexableRaw(context); 765 indexable.key = device.getName(); 766 indexable.title = device.getName(); 767 indexable.summaryOn = summary; 768 indexable.summaryOff = summary; 769 indexable.screenTitle = screenTitle; 770 indexables.add(indexable); 771 } 772 773 if (hasHardKeyboards) { 774 // Hard keyboard category. 775 indexable = new SearchIndexableRaw(context); 776 indexable.key = "builtin_keyboard_settings"; 777 indexable.title = context.getString( 778 R.string.builtin_keyboard_settings_title); 779 indexable.screenTitle = screenTitle; 780 indexables.add(indexable); 781 782 // Auto replace. 783 indexable = new SearchIndexableRaw(context); 784 indexable.key = "auto_replace"; 785 indexable.title = context.getString(R.string.auto_replace); 786 indexable.summaryOn = context.getString(R.string.auto_replace_summary); 787 indexable.summaryOff = context.getString(R.string.auto_replace_summary); 788 indexable.screenTitle = screenTitle; 789 indexables.add(indexable); 790 791 // Auto caps. 792 indexable = new SearchIndexableRaw(context); 793 indexable.key = "auto_caps"; 794 indexable.title = context.getString(R.string.auto_caps); 795 indexable.summaryOn = context.getString(R.string.auto_caps_summary); 796 indexable.summaryOff = context.getString(R.string.auto_caps_summary); 797 indexable.screenTitle = screenTitle; 798 indexables.add(indexable); 799 800 // Auto punctuate. 801 indexable = new SearchIndexableRaw(context); 802 indexable.key = "auto_punctuate"; 803 indexable.title = context.getString(R.string.auto_punctuate); 804 indexable.summaryOn = context.getString(R.string.auto_punctuate_summary); 805 indexable.summaryOff = context.getString(R.string.auto_punctuate_summary); 806 indexable.screenTitle = screenTitle; 807 indexables.add(indexable); 808 } 809 } 810 811 // Voice recognizers. 812 List<ResolveInfo> recognizers = context.getPackageManager() 813 .queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), 814 PackageManager.GET_META_DATA); 815 816 final int recognizerCount = recognizers.size(); 817 818 // Recognizer settings. 819 if (recognizerCount > 0) { 820 indexable = new SearchIndexableRaw(context); 821 indexable.key = "recognizer_settings"; 822 indexable.title = context.getString(R.string.recognizer_settings_title); 823 indexable.screenTitle = screenTitle; 824 indexables.add(indexable); 825 } 826 827 if (recognizerCount > 1) { 828 // Recognizer chooser. 829 indexable = new SearchIndexableRaw(context); 830 indexable.key = "recognizer_title"; 831 indexable.title = context.getString(R.string.recognizer_title); 832 indexable.screenTitle = screenTitle; 833 indexables.add(indexable); 834 } 835 836 for (int i = 0; i < recognizerCount; i++) { 837 ResolveInfo recognizer = recognizers.get(i); 838 839 ServiceInfo serviceInfo = recognizer.serviceInfo; 840 ComponentName componentName = new ComponentName(serviceInfo.packageName, 841 serviceInfo.name); 842 843 indexable = new SearchIndexableRaw(context); 844 indexable.key = componentName.flattenToString(); 845 indexable.title = recognizer.loadLabel(context.getPackageManager()).toString(); 846 indexable.screenTitle = screenTitle; 847 indexables.add(indexable); 848 } 849 850 // Text-to-speech. 851 TtsEngines ttsEngines = new TtsEngines(context); 852 if (!ttsEngines.getEngines().isEmpty()) { 853 indexable = new SearchIndexableRaw(context); 854 indexable.key = "tts_settings"; 855 indexable.title = context.getString(R.string.tts_settings_title); 856 indexable.screenTitle = screenTitle; 857 indexables.add(indexable); 858 } 859 860 // Pointer settings. 861 indexable = new SearchIndexableRaw(context); 862 indexable.key = "pointer_settings_category"; 863 indexable.title = context.getString(R.string.pointer_settings_category); 864 indexable.screenTitle = screenTitle; 865 indexables.add(indexable); 866 867 indexable = new SearchIndexableRaw(context); 868 indexable.key = "pointer_speed"; 869 indexable.title = context.getString(R.string.pointer_speed); 870 indexable.screenTitle = screenTitle; 871 indexables.add(indexable); 872 873 // Game controllers. 874 if (haveInputDeviceWithVibrator()) { 875 indexable = new SearchIndexableRaw(context); 876 indexable.key = "vibrate_input_devices"; 877 indexable.title = context.getString(R.string.vibrate_input_devices); 878 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary); 879 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary); 880 indexable.screenTitle = screenTitle; 881 indexables.add(indexable); 882 } 883 884 return indexables; 885 } 886 }; 887} 888