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 class Settings extends InputMethodSettingsFragment 45 implements SharedPreferences.OnSharedPreferenceChangeListener { 46 public static final boolean ENABLE_EXPERIMENTAL_SETTINGS = false; 47 48 // In the same order as xml/prefs.xml 49 public static final String PREF_GENERAL_SETTINGS = "general_settings"; 50 public static final String PREF_AUTO_CAP = "auto_cap"; 51 public static final String PREF_VIBRATE_ON = "vibrate_on"; 52 public static final String PREF_SOUND_ON = "sound_on"; 53 public static final String PREF_POPUP_ON = "popup_on"; 54 public static final String PREF_VOICE_MODE = "voice_mode"; 55 public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; 56 public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; 57 public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; 58 public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; 59 public static final String PREF_MISC_SETTINGS = "misc_settings"; 60 public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; 61 public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = 62 "last_user_dictionary_write_time"; 63 public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; 64 public static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = 65 "pref_suppress_language_switch_key"; 66 public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = 67 "pref_include_other_imes_in_language_switch_list"; 68 public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles"; 69 public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = 70 "pref_key_preview_popup_dismiss_delay"; 71 public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; 72 public static final String PREF_BIGRAM_SUGGESTION = "next_word_suggestion"; 73 public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; 74 public static final String PREF_KEY_ENABLE_SPAN_INSERT = "enable_span_insert"; 75 public static final String PREF_VIBRATION_DURATION_SETTINGS = 76 "pref_vibration_duration_settings"; 77 public static final String PREF_KEYPRESS_SOUND_VOLUME = 78 "pref_keypress_sound_volume"; 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 // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary 91 private CheckBoxPreference mBigramSuggestion; 92 // Prediction: use bigrams to predict the next word when there is no input for it yet 93 private CheckBoxPreference mBigramPrediction; 94 private Preference mDebugSettingsPreference; 95 96 private TextView mKeypressVibrationDurationSettingsTextView; 97 private TextView mKeypressSoundVolumeSettingsTextView; 98 99 private void ensureConsistencyOfAutoCorrectionSettings() { 100 final String autoCorrectionOff = getResources().getString( 101 R.string.auto_correction_threshold_mode_index_off); 102 final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); 103 mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff)); 104 if (null != mBigramPrediction) { 105 mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); 106 } 107 } 108 109 @Override 110 public void onCreate(Bundle icicle) { 111 super.onCreate(icicle); 112 setInputMethodSettingsCategoryTitle(R.string.language_selection_title); 113 setSubtypeEnablerTitle(R.string.select_language); 114 addPreferencesFromResource(R.xml.prefs); 115 116 final Resources res = getResources(); 117 final Context context = getActivity(); 118 119 mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE); 120 mShowCorrectionSuggestionsPreference = 121 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING); 122 SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 123 prefs.registerOnSharedPreferenceChangeListener(this); 124 125 mAutoCorrectionThresholdPreference = 126 (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD); 127 mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTION); 128 mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS); 129 mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS); 130 if (mDebugSettingsPreference != null) { 131 final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); 132 debugSettingsIntent.setClassName( 133 context.getPackageName(), DebugSettings.class.getName()); 134 mDebugSettingsPreference.setIntent(debugSettingsIntent); 135 } 136 137 ensureConsistencyOfAutoCorrectionSettings(); 138 139 final PreferenceGroup generalSettings = 140 (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS); 141 final PreferenceGroup textCorrectionGroup = 142 (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS); 143 final PreferenceGroup miscSettings = 144 (PreferenceGroup) findPreference(PREF_MISC_SETTINGS); 145 146 final boolean showVoiceKeyOption = res.getBoolean( 147 R.bool.config_enable_show_voice_key_option); 148 if (!showVoiceKeyOption) { 149 generalSettings.removePreference(mVoicePreference); 150 } 151 152 final PreferenceGroup advancedSettings = 153 (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS); 154 // Remove those meaningless options for now. TODO: delete them for good 155 advancedSettings.removePreference(findPreference(PREF_BIGRAM_SUGGESTION)); 156 advancedSettings.removePreference(findPreference(PREF_KEY_ENABLE_SPAN_INSERT)); 157 if (!VibratorUtils.getInstance(context).hasVibrator()) { 158 generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); 159 if (null != advancedSettings) { // Theoretically advancedSettings cannot be null 160 advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS)); 161 } 162 } 163 164 final boolean showPopupOption = res.getBoolean( 165 R.bool.config_enable_show_popup_on_keypress_option); 166 if (!showPopupOption) { 167 generalSettings.removePreference(findPreference(PREF_POPUP_ON)); 168 } 169 170 final boolean showBigramSuggestionsOption = res.getBoolean( 171 R.bool.config_enable_next_word_suggestions_option); 172 if (!showBigramSuggestionsOption) { 173 textCorrectionGroup.removePreference(mBigramSuggestion); 174 if (null != mBigramPrediction) { 175 textCorrectionGroup.removePreference(mBigramPrediction); 176 } 177 } 178 179 final CheckBoxPreference includeOtherImesInLanguageSwitchList = 180 (CheckBoxPreference)findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); 181 includeOtherImesInLanguageSwitchList.setEnabled( 182 !SettingsValues.isLanguageSwitchKeySupressed(prefs)); 183 184 mKeyPreviewPopupDismissDelay = 185 (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 186 final String[] entries = new String[] { 187 res.getString(R.string.key_preview_popup_dismiss_no_delay), 188 res.getString(R.string.key_preview_popup_dismiss_default_delay), 189 }; 190 final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( 191 R.integer.config_key_preview_linger_timeout)); 192 mKeyPreviewPopupDismissDelay.setEntries(entries); 193 mKeyPreviewPopupDismissDelay.setEntryValues( 194 new String[] { "0", popupDismissDelayDefaultValue }); 195 if (null == mKeyPreviewPopupDismissDelay.getValue()) { 196 mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); 197 } 198 mKeyPreviewPopupDismissDelay.setEnabled( 199 SettingsValues.isKeyPreviewPopupEnabled(prefs, res)); 200 201 final PreferenceScreen dictionaryLink = 202 (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY); 203 final Intent intent = dictionaryLink.getIntent(); 204 205 final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); 206 if (0 >= number) { 207 textCorrectionGroup.removePreference(dictionaryLink); 208 } 209 210 final boolean showUsabilityStudyModeOption = 211 res.getBoolean(R.bool.config_enable_usability_study_mode_option) 212 || ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS; 213 final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE); 214 if (!showUsabilityStudyModeOption) { 215 if (usabilityStudyPref != null) { 216 miscSettings.removePreference(usabilityStudyPref); 217 } 218 } 219 if (ProductionFlag.IS_EXPERIMENTAL) { 220 if (usabilityStudyPref instanceof CheckBoxPreference) { 221 CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; 222 checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true)); 223 checkbox.setSummary(R.string.settings_warning_researcher_mode); 224 } 225 } 226 227 mKeypressVibrationDurationSettingsPref = 228 (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); 229 if (mKeypressVibrationDurationSettingsPref != null) { 230 mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener( 231 new OnPreferenceClickListener() { 232 @Override 233 public boolean onPreferenceClick(Preference arg0) { 234 showKeypressVibrationDurationSettingsDialog(); 235 return true; 236 } 237 }); 238 updateKeypressVibrationDurationSettingsSummary(prefs, res); 239 } 240 241 mKeypressSoundVolumeSettingsPref = 242 (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME); 243 if (mKeypressSoundVolumeSettingsPref != null) { 244 mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener( 245 new OnPreferenceClickListener() { 246 @Override 247 public boolean onPreferenceClick(Preference arg0) { 248 showKeypressSoundVolumeSettingDialog(); 249 return true; 250 } 251 }); 252 updateKeypressSoundVolumeSummary(prefs, res); 253 } 254 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); 255 } 256 257 @Override 258 public void onResume() { 259 super.onResume(); 260 final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); 261 if (isShortcutImeEnabled) { 262 updateVoiceModeSummary(); 263 } else { 264 getPreferenceScreen().removePreference(mVoicePreference); 265 } 266 updateShowCorrectionSuggestionsSummary(); 267 updateKeyPreviewPopupDelaySummary(); 268 updateCustomInputStylesSummary(); 269 } 270 271 @Override 272 public void onDestroy() { 273 getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( 274 this); 275 super.onDestroy(); 276 } 277 278 @Override 279 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 280 (new BackupManager(getActivity())).dataChanged(); 281 if (key.equals(PREF_POPUP_ON)) { 282 final ListPreference popupDismissDelay = 283 (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 284 if (null != popupDismissDelay) { 285 popupDismissDelay.setEnabled(prefs.getBoolean(PREF_POPUP_ON, true)); 286 } 287 } else if (key.equals(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) { 288 final CheckBoxPreference includeOtherImesInLanguageSwicthList = 289 (CheckBoxPreference)findPreference( 290 PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); 291 includeOtherImesInLanguageSwicthList.setEnabled( 292 !SettingsValues.isLanguageSwitchKeySupressed(prefs)); 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 lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]); 327 } 328 329 private void updateVoiceModeSummary() { 330 mVoicePreference.setSummary( 331 getResources().getStringArray(R.array.voice_input_modes_summary) 332 [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]); 333 } 334 335 private void refreshEnablingsOfKeypressSoundAndVibrationSettings( 336 SharedPreferences sp, Resources res) { 337 if (mKeypressVibrationDurationSettingsPref != null) { 338 final boolean hasVibrator = VibratorUtils.getInstance(getActivity()).hasVibrator(); 339 final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON, 340 res.getBoolean(R.bool.config_default_vibration_enabled)); 341 mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn); 342 } 343 344 if (mKeypressSoundVolumeSettingsPref != null) { 345 final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, 346 res.getBoolean(R.bool.config_default_sound_enabled)); 347 mKeypressSoundVolumeSettingsPref.setEnabled(soundOn); 348 } 349 } 350 351 private void updateKeypressVibrationDurationSettingsSummary( 352 SharedPreferences sp, Resources res) { 353 if (mKeypressVibrationDurationSettingsPref != null) { 354 mKeypressVibrationDurationSettingsPref.setSummary( 355 SettingsValues.getCurrentVibrationDuration(sp, res) 356 + res.getString(R.string.settings_ms)); 357 } 358 } 359 360 private void showKeypressVibrationDurationSettingsDialog() { 361 final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); 362 final Context context = getActivity(); 363 final Resources res = context.getResources(); 364 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 365 builder.setTitle(R.string.prefs_keypress_vibration_duration_settings); 366 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 367 @Override 368 public void onClick(DialogInterface dialog, int whichButton) { 369 final int ms = Integer.valueOf( 370 mKeypressVibrationDurationSettingsTextView.getText().toString()); 371 sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); 372 updateKeypressVibrationDurationSettingsSummary(sp, res); 373 } 374 }); 375 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 376 @Override 377 public void onClick(DialogInterface dialog, int whichButton) { 378 dialog.dismiss(); 379 } 380 }); 381 final View v = LayoutInflater.from(context).inflate( 382 R.layout.vibration_settings_dialog, null); 383 final int currentMs = SettingsValues.getCurrentVibrationDuration( 384 getPreferenceManager().getSharedPreferences(), getResources()); 385 mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value); 386 final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings); 387 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 388 @Override 389 public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { 390 final int tempMs = arg1; 391 mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs)); 392 } 393 394 @Override 395 public void onStartTrackingTouch(SeekBar arg0) { 396 } 397 398 @Override 399 public void onStopTrackingTouch(SeekBar arg0) { 400 final int tempMs = arg0.getProgress(); 401 VibratorUtils.getInstance(context).vibrate(tempMs); 402 } 403 }); 404 sb.setProgress(currentMs); 405 mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs)); 406 builder.setView(v); 407 builder.create().show(); 408 } 409 410 private void updateKeypressSoundVolumeSummary(SharedPreferences sp, Resources res) { 411 if (mKeypressSoundVolumeSettingsPref != null) { 412 mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf( 413 (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100))); 414 } 415 } 416 417 private void showKeypressSoundVolumeSettingDialog() { 418 final Context context = getActivity(); 419 final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 420 final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); 421 final Resources res = context.getResources(); 422 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 423 builder.setTitle(R.string.prefs_keypress_sound_volume_settings); 424 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 425 @Override 426 public void onClick(DialogInterface dialog, int whichButton) { 427 final float volume = 428 ((float)Integer.valueOf( 429 mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100; 430 sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); 431 updateKeypressSoundVolumeSummary(sp, res); 432 } 433 }); 434 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 435 @Override 436 public void onClick(DialogInterface dialog, int whichButton) { 437 dialog.dismiss(); 438 } 439 }); 440 final View v = LayoutInflater.from(context).inflate( 441 R.layout.sound_effect_volume_dialog, null); 442 final int currentVolumeInt = 443 (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100); 444 mKeypressSoundVolumeSettingsTextView = 445 (TextView)v.findViewById(R.id.sound_effect_volume_value); 446 final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar); 447 sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 448 @Override 449 public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { 450 final int tempVolume = arg1; 451 mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume)); 452 } 453 454 @Override 455 public void onStartTrackingTouch(SeekBar arg0) { 456 } 457 458 @Override 459 public void onStopTrackingTouch(SeekBar arg0) { 460 final float tempVolume = ((float)arg0.getProgress()) / 100; 461 am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume); 462 } 463 }); 464 sb.setProgress(currentVolumeInt); 465 mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt)); 466 builder.setView(v); 467 builder.create().show(); 468 } 469 } 470