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