SettingsFragment.java revision 59c3ef1ff8df23e3c3e3f549c0289c479553c666
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.inputmethod.latin.settings; 18 19import android.app.Activity; 20import android.app.backup.BackupManager; 21import android.content.Context; 22import android.content.Intent; 23import android.content.SharedPreferences; 24import android.content.pm.PackageManager; 25import android.content.pm.ResolveInfo; 26import android.content.res.Resources; 27import android.media.AudioManager; 28import android.os.Build; 29import android.os.Bundle; 30import android.preference.CheckBoxPreference; 31import android.preference.ListPreference; 32import android.preference.Preference; 33import android.preference.Preference.OnPreferenceClickListener; 34import android.preference.PreferenceGroup; 35import android.preference.PreferenceScreen; 36import android.util.Log; 37import android.view.inputmethod.InputMethodSubtype; 38 39import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; 40import com.android.inputmethod.keyboard.KeyboardTheme; 41import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; 42import com.android.inputmethod.latin.R; 43import com.android.inputmethod.latin.SubtypeSwitcher; 44import com.android.inputmethod.latin.define.ProductionFlag; 45import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; 46import com.android.inputmethod.latin.userdictionary.UserDictionaryList; 47import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; 48import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 49import com.android.inputmethod.latin.utils.ApplicationUtils; 50import com.android.inputmethod.latin.utils.FeedbackUtils; 51import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 52import com.android.inputmethodcommon.InputMethodSettingsFragment; 53 54import java.util.TreeSet; 55 56public final class SettingsFragment extends InputMethodSettingsFragment 57 implements SharedPreferences.OnSharedPreferenceChangeListener { 58 private static final String TAG = SettingsFragment.class.getSimpleName(); 59 private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false; 60 private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS = 61 DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS 62 || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */; 63 64 private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { 65 final Preference preference = findPreference(preferenceKey); 66 if (preference != null) { 67 preference.setEnabled(enabled); 68 } 69 } 70 71 private void updateListPreferenceSummaryToCurrentValue(final String prefKey) { 72 // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before 73 // KitKat, we need to update the summary programmatically. 74 final ListPreference listPreference = (ListPreference)findPreference(prefKey); 75 if (listPreference == null) { 76 return; 77 } 78 final CharSequence entries[] = listPreference.getEntries(); 79 final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue()); 80 listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]); 81 } 82 83 private static void removePreference(final String preferenceKey, final PreferenceGroup parent) { 84 if (parent == null) { 85 return; 86 } 87 final Preference preference = parent.findPreference(preferenceKey); 88 if (preference != null) { 89 parent.removePreference(preference); 90 } 91 } 92 93 @Override 94 public void onCreate(final Bundle icicle) { 95 super.onCreate(icicle); 96 setInputMethodSettingsCategoryTitle(R.string.language_selection_title); 97 setSubtypeEnablerTitle(R.string.select_language); 98 addPreferencesFromResource(R.xml.prefs); 99 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 100 if (preferenceScreen != null) { 101 preferenceScreen.setTitle( 102 ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class)); 103 } 104 105 final Resources res = getResources(); 106 final Context context = getActivity(); 107 108 // When we are called from the Settings application but we are not already running, some 109 // singleton and utility classes may not have been initialized. We have to call 110 // initialization method of these classes here. See {@link LatinIME#onCreate()}. 111 SubtypeSwitcher.init(context); 112 SubtypeLocaleUtils.init(context); 113 AudioAndHapticFeedbackManager.init(context); 114 115 final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 116 prefs.registerOnSharedPreferenceChangeListener(this); 117 118 ensureConsistencyOfAutoCorrectionSettings(); 119 120 final PreferenceGroup generalSettings = 121 (PreferenceGroup) findPreference(Settings.PREF_GENERAL_SETTINGS); 122 final PreferenceGroup miscSettings = 123 (PreferenceGroup) findPreference(Settings.PREF_MISC_SETTINGS); 124 125 final Preference debugSettings = findPreference(Settings.PREF_DEBUG_SETTINGS); 126 if (debugSettings != null) { 127 if (Settings.isInternal(prefs)) { 128 final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); 129 debugSettingsIntent.setClassName( 130 context.getPackageName(), DebugSettingsActivity.class.getName()); 131 debugSettings.setIntent(debugSettingsIntent); 132 } else { 133 miscSettings.removePreference(debugSettings); 134 } 135 } 136 137 final Preference feedbackSettings = findPreference(Settings.PREF_SEND_FEEDBACK); 138 final Preference aboutSettings = findPreference(Settings.PREF_ABOUT_KEYBOARD); 139 if (feedbackSettings != null) { 140 if (FeedbackUtils.isFeedbackFormSupported()) { 141 feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() { 142 @Override 143 public boolean onPreferenceClick(final Preference pref) { 144 FeedbackUtils.showFeedbackForm(getActivity()); 145 return true; 146 } 147 }); 148 aboutSettings.setTitle(FeedbackUtils.getAboutKeyboardTitleResId()); 149 aboutSettings.setIntent(FeedbackUtils.getAboutKeyboardIntent(getActivity())); 150 } else { 151 miscSettings.removePreference(feedbackSettings); 152 miscSettings.removePreference(aboutSettings); 153 } 154 } 155 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 156 // The about screen contains items that may be confusing in development-only versions. 157 miscSettings.removePreference(aboutSettings); 158 } 159 160 final boolean showVoiceKeyOption = res.getBoolean( 161 R.bool.config_enable_show_voice_key_option); 162 if (!showVoiceKeyOption) { 163 removePreference(Settings.PREF_VOICE_INPUT_KEY, generalSettings); 164 } 165 166 final PreferenceGroup advancedSettings = 167 (PreferenceGroup) findPreference(Settings.PREF_ADVANCED_SETTINGS); 168 if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { 169 removePreference(Settings.PREF_VIBRATE_ON, generalSettings); 170 removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings); 171 } 172 173 // TODO: consolidate key preview dismiss delay with the key preview animation parameters. 174 if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { 175 removePreference(Settings.PREF_POPUP_ON, generalSettings); 176 removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings); 177 } else { 178 // TODO: Cleanup this setup. 179 final ListPreference keyPreviewPopupDismissDelay = 180 (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 181 final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( 182 R.integer.config_key_preview_linger_timeout)); 183 keyPreviewPopupDismissDelay.setEntries(new String[] { 184 res.getString(R.string.key_preview_popup_dismiss_no_delay), 185 res.getString(R.string.key_preview_popup_dismiss_default_delay), 186 }); 187 keyPreviewPopupDismissDelay.setEntryValues(new String[] { 188 "0", 189 popupDismissDelayDefaultValue 190 }); 191 if (null == keyPreviewPopupDismissDelay.getValue()) { 192 keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); 193 } 194 keyPreviewPopupDismissDelay.setEnabled( 195 Settings.readKeyPreviewPopupEnabled(prefs, res)); 196 } 197 198 if (!res.getBoolean(R.bool.config_setup_wizard_available)) { 199 removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedSettings); 200 } 201 202 setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, 203 Settings.readShowsLanguageSwitchKey(prefs)); 204 205 final PreferenceGroup textCorrectionGroup = 206 (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS); 207 final PreferenceScreen dictionaryLink = 208 (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); 209 final Intent intent = dictionaryLink.getIntent(); 210 intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName()); 211 final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); 212 if (0 >= number) { 213 textCorrectionGroup.removePreference(dictionaryLink); 214 } 215 216 final Preference editPersonalDictionary = 217 findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY); 218 final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent(); 219 final ResolveInfo ri = USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS ? null 220 : context.getPackageManager().resolveActivity( 221 editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY); 222 if (ri == null) { 223 overwriteUserDictionaryPreference(editPersonalDictionary); 224 } 225 226 if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) { 227 removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen()); 228 } 229 230 AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this); 231 232 setupKeypressVibrationDurationSettings(prefs, res); 233 setupKeypressSoundVolumeSettings(prefs, res); 234 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); 235 } 236 237 @Override 238 public void onResume() { 239 super.onResume(); 240 final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 241 final Resources res = getResources(); 242 final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY); 243 if (voiceInputKeyOption != null) { 244 final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance() 245 .isShortcutImeEnabled(); 246 voiceInputKeyOption.setEnabled(isShortcutImeEnabled); 247 voiceInputKeyOption.setSummary(isShortcutImeEnabled ? null 248 : res.getText(R.string.voice_input_disabled_summary)); 249 } 250 final CheckBoxPreference showSetupWizardIcon = 251 (CheckBoxPreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); 252 if (showSetupWizardIcon != null) { 253 showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity())); 254 } 255 updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); 256 updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 257 final ListPreference keyboardThemePref = (ListPreference)findPreference( 258 Settings.PREF_KEYBOARD_THEME); 259 if (keyboardThemePref != null) { 260 final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); 261 final String value = Integer.toString(keyboardTheme.mThemeId); 262 final CharSequence entries[] = keyboardThemePref.getEntries(); 263 final int entryIndex = keyboardThemePref.findIndexOfValue(value); 264 keyboardThemePref.setSummary(entryIndex < 0 ? null : entries[entryIndex]); 265 keyboardThemePref.setValue(value); 266 } 267 updateCustomInputStylesSummary(prefs, res); 268 } 269 270 @Override 271 public void onPause() { 272 super.onPause(); 273 final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 274 final ListPreference keyboardThemePref = (ListPreference)findPreference( 275 Settings.PREF_KEYBOARD_THEME); 276 if (keyboardThemePref != null) { 277 KeyboardTheme.saveKeyboardThemeId(keyboardThemePref.getValue(), prefs); 278 } 279 } 280 281 @Override 282 public void onDestroy() { 283 getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( 284 this); 285 super.onDestroy(); 286 } 287 288 @Override 289 public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 290 final Activity activity = getActivity(); 291 if (activity == null) { 292 // TODO: Introduce a static function to register this class and ensure that 293 // onCreate must be called before "onSharedPreferenceChanged" is called. 294 Log.w(TAG, "onSharedPreferenceChanged called before activity starts."); 295 return; 296 } 297 (new BackupManager(activity)).dataChanged(); 298 final Resources res = getResources(); 299 if (key.equals(Settings.PREF_POPUP_ON)) { 300 setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, 301 Settings.readKeyPreviewPopupEnabled(prefs, res)); 302 } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) { 303 setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, 304 Settings.readShowsLanguageSwitchKey(prefs)); 305 } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { 306 LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); 307 } 308 ensureConsistencyOfAutoCorrectionSettings(); 309 updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); 310 updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 311 updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME); 312 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); 313 } 314 315 private void ensureConsistencyOfAutoCorrectionSettings() { 316 final String autoCorrectionOff = getResources().getString( 317 R.string.auto_correction_threshold_mode_index_off); 318 final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference( 319 Settings.PREF_AUTO_CORRECTION_THRESHOLD); 320 final String currentSetting = autoCorrectionThresholdPref.getValue(); 321 setPreferenceEnabled( 322 Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff)); 323 } 324 325 private void updateCustomInputStylesSummary(final SharedPreferences prefs, 326 final Resources res) { 327 final PreferenceScreen customInputStyles = 328 (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); 329 final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); 330 final InputMethodSubtype[] subtypes = 331 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); 332 final StringBuilder styles = new StringBuilder(); 333 for (final InputMethodSubtype subtype : subtypes) { 334 if (styles.length() > 0) styles.append(", "); 335 styles.append(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); 336 } 337 customInputStyles.setSummary(styles); 338 } 339 340 private void refreshEnablingsOfKeypressSoundAndVibrationSettings( 341 final SharedPreferences sp, final Resources res) { 342 setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, 343 Settings.readVibrationEnabled(sp, res)); 344 setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME, 345 Settings.readKeypressSoundEnabled(sp, res)); 346 } 347 348 private void setupKeypressVibrationDurationSettings(final SharedPreferences sp, 349 final Resources res) { 350 final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( 351 Settings.PREF_VIBRATION_DURATION_SETTINGS); 352 if (pref == null) { 353 return; 354 } 355 pref.setInterface(new SeekBarDialogPreference.ValueProxy() { 356 @Override 357 public void writeValue(final int value, final String key) { 358 sp.edit().putInt(key, value).apply(); 359 } 360 361 @Override 362 public void writeDefaultValue(final String key) { 363 sp.edit().remove(key).apply(); 364 } 365 366 @Override 367 public int readValue(final String key) { 368 return Settings.readKeypressVibrationDuration(sp, res); 369 } 370 371 @Override 372 public int readDefaultValue(final String key) { 373 return Settings.readDefaultKeypressVibrationDuration(res); 374 } 375 376 @Override 377 public void feedbackValue(final int value) { 378 AudioAndHapticFeedbackManager.getInstance().vibrate(value); 379 } 380 381 @Override 382 public String getValueText(final int value) { 383 if (value < 0) { 384 return res.getString(R.string.settings_system_default); 385 } 386 return res.getString(R.string.abbreviation_unit_milliseconds, value); 387 } 388 }); 389 } 390 391 private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) { 392 final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( 393 Settings.PREF_KEYPRESS_SOUND_VOLUME); 394 if (pref == null) { 395 return; 396 } 397 final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE); 398 pref.setInterface(new SeekBarDialogPreference.ValueProxy() { 399 private static final float PERCENTAGE_FLOAT = 100.0f; 400 401 private float getValueFromPercentage(final int percentage) { 402 return percentage / PERCENTAGE_FLOAT; 403 } 404 405 private int getPercentageFromValue(final float floatValue) { 406 return (int)(floatValue * PERCENTAGE_FLOAT); 407 } 408 409 @Override 410 public void writeValue(final int value, final String key) { 411 sp.edit().putFloat(key, getValueFromPercentage(value)).apply(); 412 } 413 414 @Override 415 public void writeDefaultValue(final String key) { 416 sp.edit().remove(key).apply(); 417 } 418 419 @Override 420 public int readValue(final String key) { 421 return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res)); 422 } 423 424 @Override 425 public int readDefaultValue(final String key) { 426 return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res)); 427 } 428 429 @Override 430 public String getValueText(final int value) { 431 if (value < 0) { 432 return res.getString(R.string.settings_system_default); 433 } 434 return Integer.toString(value); 435 } 436 437 @Override 438 public void feedbackValue(final int value) { 439 am.playSoundEffect( 440 AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value)); 441 } 442 }); 443 } 444 445 private void overwriteUserDictionaryPreference(Preference userDictionaryPreference) { 446 final Activity activity = getActivity(); 447 final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity); 448 if (null == localeList) { 449 // The locale list is null if and only if the user dictionary service is 450 // not present or disabled. In this case we need to remove the preference. 451 getPreferenceScreen().removePreference(userDictionaryPreference); 452 } else if (localeList.size() <= 1) { 453 userDictionaryPreference.setFragment(UserDictionarySettings.class.getName()); 454 // If the size of localeList is 0, we don't set the locale parameter in the 455 // extras. This will be interpreted by the UserDictionarySettings class as 456 // meaning "the current locale". 457 // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet() 458 // the locale list always has at least one element, since it always includes the current 459 // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet(). 460 if (localeList.size() == 1) { 461 final String locale = (String)localeList.toArray()[0]; 462 userDictionaryPreference.getExtras().putString("locale", locale); 463 } 464 } else { 465 userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); 466 } 467 } 468} 469