1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.inputmethod.latin; 18 19 import android.app.AlertDialog; 20 import android.app.backup.BackupManager; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.content.SharedPreferences; 25 import android.content.res.Resources; 26 import android.media.AudioManager; 27 import android.os.Bundle; 28 import android.preference.CheckBoxPreference; 29 import android.preference.ListPreference; 30 import android.preference.Preference; 31 import android.preference.Preference.OnPreferenceClickListener; 32 import android.preference.PreferenceGroup; 33 import android.preference.PreferenceScreen; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.inputmethod.InputMethodSubtype; 37 import android.widget.SeekBar; 38 import android.widget.SeekBar.OnSeekBarChangeListener; 39 import android.widget.TextView; 40 41 import com.android.inputmethod.latin.define.ProductionFlag; 42 import com.android.inputmethodcommon.InputMethodSettingsFragment; 43 44 public final class Settings extends InputMethodSettingsFragment 45 implements SharedPreferences.OnSharedPreferenceChangeListener { 46 47 // In the same order as xml/prefs.xml 48 public static final String PREF_GENERAL_SETTINGS = "general_settings"; 49 public static final String PREF_AUTO_CAP = "auto_cap"; 50 public static final String PREF_VIBRATE_ON = "vibrate_on"; 51 public static final String PREF_SOUND_ON = "sound_on"; 52 public static final String PREF_POPUP_ON = "popup_on"; 53 public static final String PREF_VOICE_MODE = "voice_mode"; 54 public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; 55 public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; 56 public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; 57 public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; 58 public static final String PREF_MISC_SETTINGS = "misc_settings"; 59 public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = 60 "last_user_dictionary_write_time"; 61 public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; 62 public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; 63 public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = 64 "pref_show_language_switch_key"; 65 public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = 66 "pref_include_other_imes_in_language_switch_list"; 67 public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles"; 68 public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = 69 "pref_key_preview_popup_dismiss_delay"; 70 public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; 71 public static final String PREF_GESTURE_INPUT = "gesture_input"; 72 public static final String PREF_VIBRATION_DURATION_SETTINGS = 73 "pref_vibration_duration_settings"; 74 public static final String PREF_KEYPRESS_SOUND_VOLUME = 75 "pref_keypress_sound_volume"; 76 public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; 77 public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = 78 "pref_gesture_floating_preview_text"; 79 80 public static final String PREF_INPUT_LANGUAGE = "input_language"; 81 public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; 82 public static final String PREF_DEBUG_SETTINGS = "debug_settings"; 83 84 private PreferenceScreen mKeypressVibrationDurationSettingsPref; 85 private PreferenceScreen mKeypressSoundVolumeSettingsPref; 86 private ListPreference mVoicePreference; 87 private ListPreference mShowCorrectionSuggestionsPreference; 88 private ListPreference mAutoCorrectionThresholdPreference; 89 private ListPreference mKeyPreviewPopupDismissDelay; 90 // Use bigrams to predict the next word when there is no input for it yet 91 private CheckBoxPreference mBigramPrediction; 92 private Preference mDebugSettingsPreference; 93 94 private TextView mKeypressVibrationDurationSettingsTextView; 95 private TextView mKeypressSoundVolumeSettingsTextView; 96 97 private static void setPreferenceEnabled(final Preference preference, final boolean enabled) { 98 if (preference != null) { 99 preference.setEnabled(enabled); 100 } 101 } 102 103 private void ensureConsistencyOfAutoCorrectionSettings() { 104 final String autoCorrectionOff = getResources().getString( 105 R.string.auto_correction_threshold_mode_index_off); 106 final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); 107 setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff)); 108 } 109 110 @Override 111 public void onCreate(final Bundle icicle) { 112 super.onCreate(icicle); 113 setInputMethodSettingsCategoryTitle(R.string.language_selection_title); 114 setSubtypeEnablerTitle(R.string.select_language); 115 addPreferencesFromResource(R.xml.prefs); 116 117 final Resources res = getResources(); 118 final Context context = getActivity(); 119 120 // When we are called from the Settings application but we are not already running, the 121 // {@link SubtypeLocale} class may not have been initialized. It is safe to call 122 // {@link SubtypeLocale#init(Context)} multiple times. 123 SubtypeLocale.init(context); 124 mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE); 125 mShowCorrectionSuggestionsPreference = 126 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING); 127 SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 128 prefs.registerOnSharedPreferenceChangeListener(this); 129 130 mAutoCorrectionThresholdPreference = 131 (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); 132 mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); 133 ensureConsistencyOfAutoCorrectionSettings(); 134 135 final PreferenceGroup generalSettings = 136 (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS); 137 final PreferenceGroup textCorrectionGroup = 138 (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS); 139 final PreferenceGroup miscSettings = 140 (PreferenceGroup) findPreference(PREF_MISC_SETTINGS); 141 142 mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); 143 if (mDebugSettingsPreference != null) { 144 if (ProductionFlag.IS_INTERNAL) { 145 final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); 146 debugSettingsIntent.setClassName( 147 context.getPackageName(), DebugSettingsActivity.class.getName()); 148 mDebugSettingsPreference.setIntent(debugSettingsIntent); 149 } else { 150 miscSettings.removePreference(mDebugSettingsPreference); 151 } 152 } 153 154 final boolean showVoiceKeyOption = res.getBoolean( 155 R.bool.config_enable_show_voice_key_option); 156 if (!showVoiceKeyOption) { 157 generalSettings.removePreference(mVoicePreference); 158 } 159 160 final PreferenceGroup advancedSettings = 161 (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS); 162 if (!VibratorUtils.getInstance(context).hasVibrator()) { 163 generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); 164 if (null != advancedSettings) { // Theoretically advancedSettings cannot be null 165 advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS)); 166 } 167 } 168 169 final boolean showKeyPreviewPopupOption = res.getBoolean( 170 R.bool.config_enable_show_popup_on_keypress_option); 171 mKeyPreviewPopupDismissDelay = 172 (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 173 if (!showKeyPreviewPopupOption) { 174 generalSettings.removePreference(findPreference(PREF_POPUP_ON)); 175 if (null != advancedSettings) { // Theoretically advancedSettings cannot be null 176 advancedSettings.removePreference(mKeyPreviewPopupDismissDelay); 177 } 178 } else { 179 final String[] entries = new String[] { 180 res.getString(R.string.key_preview_popup_dismiss_no_delay), 181 res.getString(R.string.key_preview_popup_dismiss_default_delay), 182 }; 183 final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( 184 R.integer.config_key_preview_linger_timeout)); 185 mKeyPreviewPopupDismissDelay.setEntries(entries); 186 mKeyPreviewPopupDismissDelay.setEntryValues( 187 new String[] { "0", popupDismissDelayDefaultValue }); 188 if (null == mKeyPreviewPopupDismissDelay.getValue()) { 189 mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); 190 } 191 setPreferenceEnabled(mKeyPreviewPopupDismissDelay, 192 SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); 193 } 194 195 setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), 196 SettingsValues.showsLanguageSwitchKey(prefs)); 197 198 final PreferenceScreen dictionaryLink = 199 (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY); 200 final Intent intent = dictionaryLink.getIntent(); 201 202 final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); 203 if (0 >= number) { 204 textCorrectionGroup.removePreference(dictionaryLink); 205 } 206 207 final boolean gestureInputEnabledByBuildConfig = res.getBoolean( 208 R.bool.config_gesture_input_enabled_by_build_config); 209 final Preference gesturePreviewTrail = findPreference(PREF_GESTURE_PREVIEW_TRAIL); 210 final Preference gestureFloatingPreviewText = findPreference( 211 PREF_GESTURE_FLOATING_PREVIEW_TEXT); 212 if (!gestureInputEnabledByBuildConfig) { 213 miscSettings.removePreference(findPreference(PREF_GESTURE_INPUT)); 214 miscSettings.removePreference(gesturePreviewTrail); 215 miscSettings.removePreference(gestureFloatingPreviewText); 216 } else { 217 final boolean gestureInputEnabledByUser = prefs.getBoolean(PREF_GESTURE_INPUT, true); 218 setPreferenceEnabled(gesturePreviewTrail, gestureInputEnabledByUser); 219 setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser); 220 } 221 222 mKeypressVibrationDurationSettingsPref = 223 (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); 224 if (mKeypressVibrationDurationSettingsPref != null) { 225 mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener( 226 new OnPreferenceClickListener() { 227 @Override 228 public boolean onPreferenceClick(Preference arg0) { 229 showKeypressVibrationDurationSettingsDialog(); 230 return true; 231 } 232 }); 233 updateKeypressVibrationDurationSettingsSummary(prefs, res); 234 } 235 236 mKeypressSoundVolumeSettingsPref = 237 (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME); 238 if (mKeypressSoundVolumeSettingsPref != null) { 239 mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener( 240 new OnPreferenceClickListener() { 241 @Override 242 public boolean onPreferenceClick(Preference arg0) { 243 showKeypressSoundVolumeSettingDialog(); 244 return true; 245 } 246 }); 247 updateKeypressSoundVolumeSummary(prefs, res); 248 } 249 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); 250 } 251 252 @Override 253 public void onResume() { 254 super.onResume(); 255 final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); 256 if (isShortcutImeEnabled) { 257 updateVoiceModeSummary(); 258 } else { 259 getPreferenceScreen().removePreference(mVoicePreference); 260 } 261 updateShowCorrectionSuggestionsSummary(); 262 updateKeyPreviewPopupDelaySummary(); 263 updateCustomInputStylesSummary(); 264 } 265 266 @Override 267 public void onDestroy() { 268 getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( 269 this); 270 super.onDestroy(); 271 } 272 273 @Override 274 public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 275 (new BackupManager(getActivity())).dataChanged(); 276 if (key.equals(PREF_POPUP_ON)) { 277 setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY), 278 prefs.getBoolean(PREF_POPUP_ON, true)); 279 } else if (key.equals(PREF_SHOW_LANGUAGE_SWITCH_KEY)) { 280 setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST), 281 SettingsValues.showsLanguageSwitchKey(prefs)); 282 } else if (key.equals(PREF_GESTURE_INPUT)) { 283 final boolean gestureInputEnabledByConfig = getResources().getBoolean( 284 R.bool.config_gesture_input_enabled_by_build_config); 285 if (gestureInputEnabledByConfig) { 286 final boolean gestureInputEnabledByUser = prefs.getBoolean( 287 PREF_GESTURE_INPUT, true); 288 setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL), 289 gestureInputEnabledByUser); 290 setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT), 291 gestureInputEnabledByUser); 292 } 293 } 294 ensureConsistencyOfAutoCorrectionSettings(); 295 updateVoiceModeSummary(); 296 updateShowCorrectionSuggestionsSummary(); 297 updateKeyPreviewPopupDelaySummary(); 298 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); 299 } 300 301 private void updateShowCorrectionSuggestionsSummary() { 302 mShowCorrectionSuggestionsPreference.setSummary( 303 getResources().getStringArray(R.array.prefs_suggestion_visibilities) 304 [mShowCorrectionSuggestionsPreference.findIndexOfValue( 305 mShowCorrectionSuggestionsPreference.getValue())]); 306 } 307 308 private void updateCustomInputStylesSummary() { 309 final PreferenceScreen customInputStyles = 310 (PreferenceScreen)findPreference(PREF_CUSTOM_INPUT_STYLES); 311 final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 312 final Resources res = getResources(); 313 final String prefSubtype = SettingsValues.getPrefAdditionalSubtypes(prefs, res); 314 final InputMethodSubtype[] subtypes = 315 AdditionalSubtype.createAdditionalSubtypesArray(prefSubtype); 316 final StringBuilder styles = new StringBuilder(); 317 for (final InputMethodSubtype subtype : subtypes) { 318 if (styles.length() > 0) styles.append(", "); 319 styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res)); 320 } 321 customInputStyles.setSummary(styles); 322 } 323 324 private void updateKeyPreviewPopupDelaySummary() { 325 final ListPreference lp = mKeyPreviewPopupDismissDelay; 326 final CharSequence[] entries = lp.getEntries(); 327 if (entries == null || entries.length <= 0) return; 328 lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); 329 } 330 331 private void updateVoiceModeSummary() { 332 mVoicePreference.setSummary( 333 getResources().getStringArray(R.array.voice_input_modes_summary) 334 [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); 335 } 336 337 private void refreshEnablingsOfKeypressSoundAndVibrationSettings( 338 final SharedPreferences sp, final Resources res) { 339 if (mKeypressVibrationDurationSettingsPref != null) { 340 final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity()) 341 .hasVibrator(); 342 final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON, 343 res.getBoolean(R.bool.config_default_vibration_enabled)); 344 setPreferenceEnabled(mKeypressVibrationDurationSettingsPref, 345 hasVibratorHardware && vibrateOnByUser); 346 } 347 348 if (mKeypressSoundVolumeSettingsPref != null) { 349 final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, 350 res.getBoolean(R.bool.config_default_sound_enabled)); 351 setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn); 352 } 353 } 354 355 private void updateKeypressVibrationDurationSettingsSummary( 356 final SharedPreferences sp, final Resources res) { 357 if (mKeypressVibrationDurationSettingsPref != null) { 358 mKeypressVibrationDurationSettingsPref.setSummary( 359 SettingsValues.getCurrentVibrationDuration(sp, res) 360 + res.getString(R.string.settings_ms)); 361 } 362 } 363 364 private void showKeypressVibrationDurationSettingsDialog() { 365 final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); 366 final Context context = getActivity(); 367 final Resources res = context.getResources(); 368 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 369 builder.setTitle(R.string.prefs_keypress_vibration_duration_settings); 370 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 371 @Override 372 public void onClick(DialogInterface dialog, int whichButton) { 373 final int ms = Integer.valueOf( 374 mKeypressVibrationDurationSettingsTextView.getText().toString()); 375 sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); 376 updateKeypressVibrationDurationSettingsSummary(sp, res); 377 } 378 }); 379 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 380 @Override 381 public void onClick(DialogInterface dialog, int whichButton) { 382 dialog.dismiss(); 383 } 384 }); 385 final View v = LayoutInflater.from(context).inflate( 386 R.layout.vibration_settings_dialog, null); 387 final int currentMs = SettingsValues.getCurrentVibrationDuration( 388 getPreferenceManager().getSharedPreferences(), getResources()); 389 mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value); 390 final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings); 391 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 392 @Override 393 public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { 394 final int tempMs = arg1; 395 mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs)); 396 } 397 398 @Override 399 public void onStartTrackingTouch(SeekBar arg0) { 400 } 401 402 @Override 403 public void onStopTrackingTouch(SeekBar arg0) { 404 final int tempMs = arg0.getProgress(); 405 VibratorUtils.getInstance(context).vibrate(tempMs); 406 } 407 }); 408 sb.setProgress(currentMs); 409 mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs)); 410 builder.setView(v); 411 builder.create().show(); 412 } 413 414 private void updateKeypressSoundVolumeSummary(final SharedPreferences sp, final Resources res) { 415 if (mKeypressSoundVolumeSettingsPref != null) { 416 mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf( 417 (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100))); 418 } 419 } 420 421 private void showKeypressSoundVolumeSettingDialog() { 422 final Context context = getActivity(); 423 final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 424 final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); 425 final Resources res = context.getResources(); 426 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 427 builder.setTitle(R.string.prefs_keypress_sound_volume_settings); 428 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 429 @Override 430 public void onClick(DialogInterface dialog, int whichButton) { 431 final float volume = 432 ((float)Integer.valueOf( 433 mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100; 434 sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); 435 updateKeypressSoundVolumeSummary(sp, res); 436 } 437 }); 438 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 439 @Override 440 public void onClick(DialogInterface dialog, int whichButton) { 441 dialog.dismiss(); 442 } 443 }); 444 final View v = LayoutInflater.from(context).inflate( 445 R.layout.sound_effect_volume_dialog, null); 446 final int currentVolumeInt = 447 (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100); 448 mKeypressSoundVolumeSettingsTextView = 449 (TextView)v.findViewById(R.id.sound_effect_volume_value); 450 final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar); 451 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 452 @Override 453 public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { 454 final int tempVolume = arg1; 455 mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume)); 456 } 457 458 @Override 459 public void onStartTrackingTouch(SeekBar arg0) { 460 } 461 462 @Override 463 public void onStopTrackingTouch(SeekBar arg0) { 464 final float tempVolume = ((float)arg0.getProgress()) / 100; 465 am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume); 466 } 467 }); 468 sb.setProgress(currentVolumeInt); 469 mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt)); 470 builder.setView(v); 471 builder.create().show(); 472 } 473 } 474