InputMethodAndLanguageSettings.java revision 96cdcd80a8ba9d9a808a712037c3e4fa3fdcf084
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 com.android.settings.R; 20import com.android.settings.Settings.KeyboardLayoutPickerActivity; 21import com.android.settings.Settings.SpellCheckersSettingsActivity; 22import com.android.settings.SettingsPreferenceFragment; 23import com.android.settings.Utils; 24import com.android.settings.VoiceInputOutputSettings; 25 26import android.app.Activity; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.pm.PackageManager; 31import android.content.res.Configuration; 32import android.database.ContentObserver; 33import android.hardware.input.InputManager; 34import android.hardware.input.KeyboardLayout; 35import android.os.Bundle; 36import android.os.Handler; 37import android.preference.CheckBoxPreference; 38import android.preference.ListPreference; 39import android.preference.Preference; 40import android.preference.PreferenceCategory; 41import android.preference.PreferenceScreen; 42import android.provider.Settings; 43import android.provider.Settings.System; 44import android.text.TextUtils; 45import android.view.InputDevice; 46import android.view.inputmethod.InputMethodInfo; 47import android.view.inputmethod.InputMethodManager; 48 49import java.util.ArrayList; 50import java.util.Collections; 51import java.util.List; 52import java.util.Set; 53 54public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment 55 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener { 56 57 private static final String KEY_PHONE_LANGUAGE = "phone_language"; 58 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method"; 59 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector"; 60 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings"; 61 // false: on ICS or later 62 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false; 63 64 private static final String[] sSystemSettingNames = { 65 System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, 66 }; 67 68 private static final String[] sHardKeyboardKeys = { 69 "auto_replace", "auto_caps", "auto_punctuate", 70 }; 71 72 private int mDefaultInputMethodSelectorVisibility = 0; 73 private ListPreference mShowInputMethodSelectorPref; 74 private PreferenceCategory mKeyboardSettingsCategory; 75 private PreferenceCategory mHardKeyboardCategory; 76 private Preference mLanguagePref; 77 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = 78 new ArrayList<InputMethodPreference>(); 79 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = 80 new ArrayList<PreferenceScreen>(); 81 private InputManager mIm; 82 private InputMethodManager mImm; 83 private List<InputMethodInfo> mImis; 84 private boolean mIsOnlyImeSettings; 85 private Handler mHandler; 86 @SuppressWarnings("unused") 87 private SettingsObserver mSettingsObserver; 88 89 @Override 90 public void onCreate(Bundle icicle) { 91 super.onCreate(icicle); 92 93 addPreferencesFromResource(R.xml.language_settings); 94 95 try { 96 mDefaultInputMethodSelectorVisibility = Integer.valueOf( 97 getString(R.string.input_method_selector_visibility_default_value)); 98 } catch (NumberFormatException e) { 99 } 100 101 if (getActivity().getAssets().getLocales().length == 1) { 102 // No "Select language" pref if there's only one system locale available. 103 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE)); 104 } else { 105 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE); 106 } 107 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 108 mShowInputMethodSelectorPref = (ListPreference)findPreference( 109 KEY_INPUT_METHOD_SELECTOR); 110 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); 111 // TODO: Update current input method name on summary 112 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility()); 113 } 114 115 new VoiceInputOutputSettings(this).onCreate(); 116 117 // Get references to dynamically constructed categories. 118 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard"); 119 mKeyboardSettingsCategory = (PreferenceCategory)findPreference( 120 "keyboard_settings_category"); 121 122 // Filter out irrelevant features if invoked from IME settings button. 123 mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( 124 getActivity().getIntent().getAction()); 125 getActivity().getIntent().setAction(null); 126 if (mIsOnlyImeSettings) { 127 getPreferenceScreen().removeAll(); 128 getPreferenceScreen().addPreference(mHardKeyboardCategory); 129 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 130 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); 131 } 132 getPreferenceScreen().addPreference(mKeyboardSettingsCategory); 133 } 134 135 // Build IME preference category. 136 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 137 mImis = mImm.getInputMethodList(); 138 139 mKeyboardSettingsCategory.removeAll(); 140 if (!mIsOnlyImeSettings) { 141 final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null); 142 currentIme.setKey(KEY_CURRENT_INPUT_METHOD); 143 currentIme.setTitle(getResources().getString(R.string.current_input_method)); 144 mKeyboardSettingsCategory.addPreference(currentIme); 145 } 146 147 mInputMethodPreferenceList.clear(); 148 final int N = (mImis == null ? 0 : mImis.size()); 149 for (int i = 0; i < N; ++i) { 150 final InputMethodInfo imi = mImis.get(i); 151 final InputMethodPreference pref = getInputMethodPreference(imi, N); 152 mInputMethodPreferenceList.add(pref); 153 } 154 155 if (!mInputMethodPreferenceList.isEmpty()) { 156 Collections.sort(mInputMethodPreferenceList); 157 for (int i = 0; i < N; ++i) { 158 mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i)); 159 } 160 } 161 162 // Build hard keyboard preference category. 163 mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE); 164 updateHardKeyboards(); 165 166 // Spell Checker 167 final Intent intent = new Intent(Intent.ACTION_MAIN); 168 intent.setClass(getActivity(), SpellCheckersSettingsActivity.class); 169 final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference( 170 "spellcheckers_settings")); 171 if (scp != null) { 172 scp.setFragmentIntent(this, intent); 173 } 174 175 mHandler = new Handler(); 176 mSettingsObserver = new SettingsObserver(mHandler, getActivity()); 177 } 178 179 private void updateInputMethodSelectorSummary(int value) { 180 String[] inputMethodSelectorTitles = getResources().getStringArray( 181 R.array.input_method_selector_titles); 182 if (inputMethodSelectorTitles.length > value) { 183 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]); 184 mShowInputMethodSelectorPref.setValue(String.valueOf(value)); 185 } 186 } 187 188 private void updateUserDictionaryPreference(Preference userDictionaryPreference) { 189 final Activity activity = getActivity(); 190 final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity); 191 if (null == localeList) { 192 // The locale list is null if and only if the user dictionary service is 193 // not present or disabled. In this case we need to remove the preference. 194 getPreferenceScreen().removePreference(userDictionaryPreference); 195 } else if (localeList.size() <= 1) { 196 final Intent intent = 197 new Intent(UserDictionaryList.USER_DICTIONARY_SETTINGS_INTENT_ACTION); 198 userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title); 199 userDictionaryPreference.setIntent(intent); 200 // If the size of localeList is 0, we don't set the locale parameter in the 201 // extras. This will be interpreted by the UserDictionarySettings class as 202 // meaning "the current locale". 203 // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesList() 204 // the locale list always has at least one element, since it always includes the current 205 // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesList(). 206 if (localeList.size() == 1) { 207 final String locale = (String)localeList.toArray()[0]; 208 userDictionaryPreference.getExtras().putString("locale", locale); 209 } 210 } else { 211 userDictionaryPreference.setTitle(R.string.user_dict_multiple_settings_title); 212 userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); 213 } 214 } 215 216 @Override 217 public void onResume() { 218 super.onResume(); 219 220 mIm.registerInputDeviceListener(this, null); 221 222 if (!mIsOnlyImeSettings) { 223 if (mLanguagePref != null) { 224 Configuration conf = getResources().getConfiguration(); 225 String locale = conf.locale.getDisplayName(conf.locale); 226 if (locale != null && locale.length() > 1) { 227 locale = Character.toUpperCase(locale.charAt(0)) + locale.substring(1); 228 mLanguagePref.setSummary(locale); 229 } 230 } 231 232 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); 233 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 234 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); 235 } 236 } 237 238 // Hard keyboard 239 if (!mHardKeyboardPreferenceList.isEmpty()) { 240 for (int i = 0; i < sHardKeyboardKeys.length; ++i) { 241 CheckBoxPreference chkPref = (CheckBoxPreference) 242 mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); 243 chkPref.setChecked( 244 System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); 245 } 246 } 247 248 updateHardKeyboards(); 249 250 // IME 251 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( 252 this, getContentResolver(), mImis, null); 253 updateActiveInputMethodsSummary(); 254 } 255 256 @Override 257 public void onPause() { 258 super.onPause(); 259 260 mIm.unregisterInputDeviceListener(this); 261 262 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 263 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); 264 } 265 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( 266 this, getContentResolver(), mImis, !mHardKeyboardPreferenceList.isEmpty()); 267 } 268 269 @Override 270 public void onInputDeviceAdded(int deviceId) { 271 updateHardKeyboards(); 272 } 273 274 @Override 275 public void onInputDeviceChanged(int deviceId) { 276 updateHardKeyboards(); 277 } 278 279 @Override 280 public void onInputDeviceRemoved(int deviceId) { 281 updateHardKeyboards(); 282 } 283 284 @Override 285 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 286 // Input Method stuff 287 if (Utils.isMonkeyRunning()) { 288 return false; 289 } 290 if (preference instanceof PreferenceScreen) { 291 if (preference.getFragment() != null) { 292 // Fragment will be handled correctly by the super class. 293 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) { 294 final InputMethodManager imm = (InputMethodManager) 295 getSystemService(Context.INPUT_METHOD_SERVICE); 296 imm.showInputMethodPicker(); 297 } 298 } else if (preference instanceof CheckBoxPreference) { 299 final CheckBoxPreference chkPref = (CheckBoxPreference) preference; 300 if (!mHardKeyboardPreferenceList.isEmpty()) { 301 for (int i = 0; i < sHardKeyboardKeys.length; ++i) { 302 if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { 303 System.putInt(getContentResolver(), sSystemSettingNames[i], 304 chkPref.isChecked() ? 1 : 0); 305 return true; 306 } 307 } 308 } 309 } 310 return super.onPreferenceTreeClick(preferenceScreen, preference); 311 } 312 313 private void saveInputMethodSelectorVisibility(String value) { 314 try { 315 int intValue = Integer.valueOf(value); 316 Settings.Secure.putInt(getContentResolver(), 317 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue); 318 updateInputMethodSelectorSummary(intValue); 319 } catch(NumberFormatException e) { 320 } 321 } 322 323 private int loadInputMethodSelectorVisibility() { 324 return Settings.Secure.getInt(getContentResolver(), 325 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, 326 mDefaultInputMethodSelectorVisibility); 327 } 328 329 @Override 330 public boolean onPreferenceChange(Preference preference, Object value) { 331 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { 332 if (preference == mShowInputMethodSelectorPref) { 333 if (value instanceof String) { 334 saveInputMethodSelectorVisibility((String)value); 335 } 336 } 337 } 338 return false; 339 } 340 341 private void updateActiveInputMethodsSummary() { 342 for (Preference pref : mInputMethodPreferenceList) { 343 if (pref instanceof InputMethodPreference) { 344 ((InputMethodPreference)pref).updateSummary(); 345 } 346 } 347 updateCurrentImeName(); 348 } 349 350 private void updateCurrentImeName() { 351 final Context context = getActivity(); 352 if (context == null || mImm == null) return; 353 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD); 354 if (curPref != null) { 355 final CharSequence curIme = InputMethodAndSubtypeUtil.getCurrentInputMethodName( 356 context, getContentResolver(), mImm, mImis, getPackageManager()); 357 if (!TextUtils.isEmpty(curIme)) { 358 synchronized(this) { 359 curPref.setSummary(curIme); 360 } 361 } 362 } 363 } 364 365 private InputMethodPreference getInputMethodPreference(InputMethodInfo imi, int imiSize) { 366 final PackageManager pm = getPackageManager(); 367 final CharSequence label = imi.loadLabel(pm); 368 // IME settings 369 final Intent intent; 370 final String settingsActivity = imi.getSettingsActivity(); 371 if (!TextUtils.isEmpty(settingsActivity)) { 372 intent = new Intent(Intent.ACTION_MAIN); 373 intent.setClassName(imi.getPackageName(), settingsActivity); 374 } else { 375 intent = null; 376 } 377 378 // Add a check box for enabling/disabling IME 379 InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi, imiSize); 380 pref.setKey(imi.getId()); 381 pref.setTitle(label); 382 return pref; 383 } 384 385 private void updateHardKeyboards() { 386 mHardKeyboardPreferenceList.clear(); 387 if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { 388 final int[] devices = InputDevice.getDeviceIds(); 389 for (int i = 0; i < devices.length; i++) { 390 InputDevice device = InputDevice.getDevice(devices[i]); 391 if (device != null 392 && !device.isVirtual() 393 && (device.getSources() & InputDevice.SOURCE_KEYBOARD) != 0 394 && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { 395 final String inputDeviceDescriptor = device.getDescriptor(); 396 final String keyboardLayoutDescriptor = 397 mIm.getKeyboardLayoutForInputDevice(inputDeviceDescriptor); 398 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? 399 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null; 400 401 final Intent intent = new Intent(Intent.ACTION_MAIN); 402 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class); 403 intent.putExtra(KeyboardLayoutPicker.EXTRA_INPUT_DEVICE_DESCRIPTOR, 404 inputDeviceDescriptor); 405 406 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null); 407 pref.setTitle(device.getName()); 408 if (keyboardLayout != null) { 409 pref.setSummary(keyboardLayout.getLabel()); 410 } 411 pref.setIntent(intent); 412 mHardKeyboardPreferenceList.add(pref); 413 } 414 } 415 } 416 417 if (!mHardKeyboardPreferenceList.isEmpty()) { 418 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) { 419 final Preference pref = mHardKeyboardCategory.getPreference(i); 420 if (pref.getOrder() < 1000) { 421 mHardKeyboardCategory.removePreference(pref); 422 } 423 } 424 425 Collections.sort(mHardKeyboardPreferenceList); 426 final int count = mHardKeyboardPreferenceList.size(); 427 for (int i = 0; i < count; i++) { 428 final Preference pref = mHardKeyboardPreferenceList.get(i); 429 pref.setOrder(i); 430 mHardKeyboardCategory.addPreference(pref); 431 } 432 433 getPreferenceScreen().addPreference(mHardKeyboardCategory); 434 } else { 435 getPreferenceScreen().removePreference(mHardKeyboardCategory); 436 } 437 } 438 439 private class SettingsObserver extends ContentObserver { 440 public SettingsObserver(Handler handler, Context context) { 441 super(handler); 442 final ContentResolver cr = context.getContentResolver(); 443 cr.registerContentObserver( 444 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this); 445 cr.registerContentObserver(Settings.Secure.getUriFor( 446 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); 447 } 448 449 @Override public void onChange(boolean selfChange) { 450 updateCurrentImeName(); 451 } 452 } 453} 454