SettingsFragment.java revision 5a5ee95faead8a2ae749067716481e86faf5f113
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.ListPreference; 31import android.preference.Preference; 32import android.preference.PreferenceGroup; 33import android.preference.PreferenceScreen; 34import android.preference.TwoStatePreference; 35import android.util.Log; 36import android.view.Menu; 37import android.view.MenuInflater; 38import android.view.MenuItem; 39import android.view.inputmethod.InputMethodSubtype; 40 41import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; 42import com.android.inputmethod.keyboard.KeyboardTheme; 43import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; 44import com.android.inputmethod.latin.R; 45import com.android.inputmethod.latin.SubtypeSwitcher; 46import com.android.inputmethod.latin.define.ProductionFlags; 47import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; 48import com.android.inputmethod.latin.userdictionary.UserDictionaryList; 49import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; 50import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 51import com.android.inputmethod.latin.utils.ApplicationUtils; 52import com.android.inputmethod.latin.utils.FeedbackUtils; 53import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 54import com.android.inputmethodcommon.InputMethodSettingsFragment; 55 56import java.util.TreeSet; 57 58public final class SettingsFragment extends InputMethodSettingsFragment 59 implements SharedPreferences.OnSharedPreferenceChangeListener { 60 private static final String TAG = SettingsFragment.class.getSimpleName(); 61 private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false; 62 private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS = 63 DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS 64 || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; 65 66 private static final int NO_MENU_GROUP = Menu.NONE; // We don't care about menu grouping. 67 private static final int MENU_FEEDBACK = Menu.FIRST; // The first menu item id and order. 68 private static final int MENU_ABOUT = Menu.FIRST + 1; // The second menu item id and order. 69 70 private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { 71 final Preference preference = findPreference(preferenceKey); 72 if (preference != null) { 73 preference.setEnabled(enabled); 74 } 75 } 76 77 private void updateListPreferenceSummaryToCurrentValue(final String prefKey) { 78 // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before 79 // KitKat, we need to update the summary programmatically. 80 final ListPreference listPreference = (ListPreference)findPreference(prefKey); 81 if (listPreference == null) { 82 return; 83 } 84 final CharSequence entries[] = listPreference.getEntries(); 85 final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue()); 86 listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]); 87 } 88 89 private static void removePreference(final String preferenceKey, final PreferenceGroup parent) { 90 if (parent == null) { 91 return; 92 } 93 final Preference preference = parent.findPreference(preferenceKey); 94 if (preference != null) { 95 parent.removePreference(preference); 96 } 97 } 98 99 @Override 100 public void onCreate(final Bundle icicle) { 101 super.onCreate(icicle); 102 setHasOptionsMenu(true); 103 setInputMethodSettingsCategoryTitle(R.string.language_selection_title); 104 setSubtypeEnablerTitle(R.string.select_language); 105 addPreferencesFromResource(R.xml.prefs); 106 final PreferenceScreen preferenceScreen = getPreferenceScreen(); 107 TwoStatePreferenceHelper.replaceCheckBoxPreferencesBySwitchPreferences(preferenceScreen); 108 preferenceScreen.setTitle( 109 ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class)); 110 111 final Resources res = getResources(); 112 final Context context = getActivity(); 113 114 // When we are called from the Settings application but we are not already running, some 115 // singleton and utility classes may not have been initialized. We have to call 116 // initialization method of these classes here. See {@link LatinIME#onCreate()}. 117 SubtypeSwitcher.init(context); 118 SubtypeLocaleUtils.init(context); 119 AudioAndHapticFeedbackManager.init(context); 120 121 final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); 122 prefs.registerOnSharedPreferenceChangeListener(this); 123 124 ensureConsistencyOfAutoCorrectionSettings(); 125 126 final PreferenceScreen inputScreen = 127 (PreferenceScreen) findPreference(Settings.SCREEN_INPUT); 128 final PreferenceScreen multiLingualScreen = 129 (PreferenceScreen) findPreference(Settings.SCREEN_MULTI_LINGUAL); 130 final PreferenceScreen gestureScreen = 131 (PreferenceScreen) findPreference(Settings.SCREEN_GESTURE); 132 final PreferenceScreen correctionScreen = 133 (PreferenceScreen) findPreference(Settings.SCREEN_CORRECTION); 134 final PreferenceScreen advancedScreen = 135 (PreferenceScreen) findPreference(Settings.SCREEN_ADVANCED); 136 final PreferenceScreen debugScreen = 137 (PreferenceScreen) findPreference(Settings.SCREEN_DEBUG); 138 139 if (Settings.isInternal(prefs)) { 140 final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); 141 debugSettingsIntent.setClassName( 142 context.getPackageName(), DebugSettingsActivity.class.getName()); 143 debugScreen.setIntent(debugSettingsIntent); 144 } else { 145 advancedScreen.removePreference(debugScreen); 146 } 147 148 final boolean showVoiceKeyOption = res.getBoolean( 149 R.bool.config_enable_show_voice_key_option); 150 if (!showVoiceKeyOption) { 151 removePreference(Settings.PREF_VOICE_INPUT_KEY, inputScreen); 152 } 153 154 if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { 155 removePreference(Settings.PREF_VIBRATE_ON, inputScreen); 156 removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedScreen); 157 } 158 if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) { 159 removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, multiLingualScreen); 160 removePreference( 161 Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, multiLingualScreen); 162 } 163 164 // TODO: consolidate key preview dismiss delay with the key preview animation parameters. 165 if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { 166 removePreference(Settings.PREF_POPUP_ON, inputScreen); 167 removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedScreen); 168 } else { 169 // TODO: Cleanup this setup. 170 final ListPreference keyPreviewPopupDismissDelay = 171 (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 172 final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( 173 R.integer.config_key_preview_linger_timeout)); 174 keyPreviewPopupDismissDelay.setEntries(new String[] { 175 res.getString(R.string.key_preview_popup_dismiss_no_delay), 176 res.getString(R.string.key_preview_popup_dismiss_default_delay), 177 }); 178 keyPreviewPopupDismissDelay.setEntryValues(new String[] { 179 "0", 180 popupDismissDelayDefaultValue 181 }); 182 if (null == keyPreviewPopupDismissDelay.getValue()) { 183 keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); 184 } 185 keyPreviewPopupDismissDelay.setEnabled( 186 Settings.readKeyPreviewPopupEnabled(prefs, res)); 187 } 188 189 if (!res.getBoolean(R.bool.config_setup_wizard_available)) { 190 removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedScreen); 191 } 192 193 final PreferenceScreen dictionaryLink = 194 (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); 195 final Intent intent = dictionaryLink.getIntent(); 196 intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName()); 197 final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); 198 if (0 >= number) { 199 correctionScreen.removePreference(dictionaryLink); 200 } 201 202 if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) { 203 final Preference enableMetricsLogging = 204 findPreference(Settings.PREF_ENABLE_METRICS_LOGGING); 205 if (enableMetricsLogging != null) { 206 final int applicationLabelRes = context.getApplicationInfo().labelRes; 207 final String applicationName = res.getString(applicationLabelRes); 208 final String enableMetricsLoggingTitle = res.getString( 209 R.string.enable_metrics_logging, applicationName); 210 enableMetricsLogging.setTitle(enableMetricsLoggingTitle); 211 } 212 } else { 213 removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, advancedScreen); 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 getPreferenceScreen().removePreference(gestureScreen); 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 TwoStatePreference showSetupWizardIcon = 251 (TwoStatePreference)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_SETUP_WIZARD_ICON)) { 303 LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); 304 } 305 ensureConsistencyOfAutoCorrectionSettings(); 306 updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); 307 updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); 308 updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME); 309 refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); 310 } 311 312 private void ensureConsistencyOfAutoCorrectionSettings() { 313 final String autoCorrectionOff = getResources().getString( 314 R.string.auto_correction_threshold_mode_index_off); 315 final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference( 316 Settings.PREF_AUTO_CORRECTION_THRESHOLD); 317 final String currentSetting = autoCorrectionThresholdPref.getValue(); 318 setPreferenceEnabled( 319 Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff)); 320 } 321 322 private void updateCustomInputStylesSummary(final SharedPreferences prefs, 323 final Resources res) { 324 final PreferenceScreen customInputStyles = 325 (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); 326 final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); 327 final InputMethodSubtype[] subtypes = 328 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); 329 final StringBuilder styles = new StringBuilder(); 330 for (final InputMethodSubtype subtype : subtypes) { 331 if (styles.length() > 0) styles.append(", "); 332 styles.append(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); 333 } 334 customInputStyles.setSummary(styles); 335 } 336 337 private void refreshEnablingsOfKeypressSoundAndVibrationSettings( 338 final SharedPreferences sp, final Resources res) { 339 setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, 340 Settings.readVibrationEnabled(sp, res)); 341 setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME, 342 Settings.readKeypressSoundEnabled(sp, res)); 343 } 344 345 private void setupKeypressVibrationDurationSettings(final SharedPreferences sp, 346 final Resources res) { 347 final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( 348 Settings.PREF_VIBRATION_DURATION_SETTINGS); 349 if (pref == null) { 350 return; 351 } 352 pref.setInterface(new SeekBarDialogPreference.ValueProxy() { 353 @Override 354 public void writeValue(final int value, final String key) { 355 sp.edit().putInt(key, value).apply(); 356 } 357 358 @Override 359 public void writeDefaultValue(final String key) { 360 sp.edit().remove(key).apply(); 361 } 362 363 @Override 364 public int readValue(final String key) { 365 return Settings.readKeypressVibrationDuration(sp, res); 366 } 367 368 @Override 369 public int readDefaultValue(final String key) { 370 return Settings.readDefaultKeypressVibrationDuration(res); 371 } 372 373 @Override 374 public void feedbackValue(final int value) { 375 AudioAndHapticFeedbackManager.getInstance().vibrate(value); 376 } 377 378 @Override 379 public String getValueText(final int value) { 380 if (value < 0) { 381 return res.getString(R.string.settings_system_default); 382 } 383 return res.getString(R.string.abbreviation_unit_milliseconds, value); 384 } 385 }); 386 } 387 388 private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) { 389 final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( 390 Settings.PREF_KEYPRESS_SOUND_VOLUME); 391 if (pref == null) { 392 return; 393 } 394 final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE); 395 pref.setInterface(new SeekBarDialogPreference.ValueProxy() { 396 private static final float PERCENTAGE_FLOAT = 100.0f; 397 398 private float getValueFromPercentage(final int percentage) { 399 return percentage / PERCENTAGE_FLOAT; 400 } 401 402 private int getPercentageFromValue(final float floatValue) { 403 return (int)(floatValue * PERCENTAGE_FLOAT); 404 } 405 406 @Override 407 public void writeValue(final int value, final String key) { 408 sp.edit().putFloat(key, getValueFromPercentage(value)).apply(); 409 } 410 411 @Override 412 public void writeDefaultValue(final String key) { 413 sp.edit().remove(key).apply(); 414 } 415 416 @Override 417 public int readValue(final String key) { 418 return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res)); 419 } 420 421 @Override 422 public int readDefaultValue(final String key) { 423 return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res)); 424 } 425 426 @Override 427 public String getValueText(final int value) { 428 if (value < 0) { 429 return res.getString(R.string.settings_system_default); 430 } 431 return Integer.toString(value); 432 } 433 434 @Override 435 public void feedbackValue(final int value) { 436 am.playSoundEffect( 437 AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value)); 438 } 439 }); 440 } 441 442 private void overwriteUserDictionaryPreference(Preference userDictionaryPreference) { 443 final Activity activity = getActivity(); 444 final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity); 445 if (null == localeList) { 446 // The locale list is null if and only if the user dictionary service is 447 // not present or disabled. In this case we need to remove the preference. 448 getPreferenceScreen().removePreference(userDictionaryPreference); 449 } else if (localeList.size() <= 1) { 450 userDictionaryPreference.setFragment(UserDictionarySettings.class.getName()); 451 // If the size of localeList is 0, we don't set the locale parameter in the 452 // extras. This will be interpreted by the UserDictionarySettings class as 453 // meaning "the current locale". 454 // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet() 455 // the locale list always has at least one element, since it always includes the current 456 // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet(). 457 if (localeList.size() == 1) { 458 final String locale = (String)localeList.toArray()[0]; 459 userDictionaryPreference.getExtras().putString("locale", locale); 460 } 461 } else { 462 userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); 463 } 464 } 465 466 @Override 467 public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 468 if (FeedbackUtils.isFeedbackFormSupported()) { 469 menu.add(NO_MENU_GROUP, MENU_FEEDBACK /* itemId */, MENU_FEEDBACK /* order */, 470 R.string.send_feedback); 471 } 472 final int aboutResId = FeedbackUtils.getAboutKeyboardTitleResId(); 473 if (aboutResId != 0) { 474 menu.add(NO_MENU_GROUP, MENU_ABOUT /* itemId */, MENU_ABOUT /* order */, aboutResId); 475 } 476 } 477 478 @Override 479 public boolean onOptionsItemSelected(final MenuItem item) { 480 final int itemId = item.getItemId(); 481 if (itemId == MENU_FEEDBACK) { 482 FeedbackUtils.showFeedbackForm(getActivity()); 483 return true; 484 } 485 if (itemId == MENU_ABOUT) { 486 final Intent aboutIntent = FeedbackUtils.getAboutKeyboardIntent(getActivity()); 487 if (aboutIntent != null) { 488 startActivity(aboutIntent); 489 return true; 490 } 491 } 492 return super.onOptionsItemSelected(item); 493 } 494} 495