Settings.java revision f1f5ed542d43566f30e9f03f98de9bef717465ce
1/* 2 * Copyright (C) 2013 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.content.Context; 20import android.content.SharedPreferences; 21import android.content.pm.ApplicationInfo; 22import android.content.res.Resources; 23import android.preference.PreferenceManager; 24import android.util.Log; 25 26import com.android.inputmethod.keyboard.KeyboardSwitcher; 27import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; 28import com.android.inputmethod.latin.InputAttributes; 29import com.android.inputmethod.latin.R; 30import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 31import com.android.inputmethod.latin.utils.ResourceUtils; 32import com.android.inputmethod.latin.utils.RunInLocale; 33import com.android.inputmethod.latin.utils.StringUtils; 34 35import java.util.Collections; 36import java.util.Locale; 37import java.util.Set; 38import java.util.concurrent.locks.ReentrantLock; 39 40public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { 41 private static final String TAG = Settings.class.getSimpleName(); 42 // In the same order as xml/prefs.xml 43 public static final String PREF_GENERAL_SETTINGS = "general_settings"; 44 public static final String PREF_AUTO_CAP = "auto_cap"; 45 public static final String PREF_VIBRATE_ON = "vibrate_on"; 46 public static final String PREF_SOUND_ON = "sound_on"; 47 public static final String PREF_POPUP_ON = "popup_on"; 48 // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead. 49 public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode"; 50 public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key"; 51 public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; 52 public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary"; 53 public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; 54 public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; 55 public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; 56 public static final String PREF_MISC_SETTINGS = "misc_settings"; 57 public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; 58 public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; 59 public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts"; 60 public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD = 61 "pref_key_use_double_space_period"; 62 public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = 63 "pref_key_block_potentially_offensive"; 64 public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = 65 "pref_show_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_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; 69 public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles"; 70 // TODO: consolidate key preview dismiss delay with the key preview animation parameters. 71 public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = 72 "pref_key_preview_popup_dismiss_delay"; 73 public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; 74 public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings"; 75 public static final String PREF_GESTURE_INPUT = "gesture_input"; 76 public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview"; 77 public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; 78 public static final String PREF_VIBRATION_DURATION_SETTINGS = 79 "pref_vibration_duration_settings"; 80 public static final String PREF_KEYPRESS_SOUND_VOLUME = 81 "pref_keypress_sound_volume"; 82 public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; 83 public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = 84 "pref_gesture_floating_preview_text"; 85 public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon"; 86 public static final String PREF_PHRASE_GESTURE_ENABLED = "pref_gesture_space_aware"; 87 88 public static final String PREF_INPUT_LANGUAGE = "input_language"; 89 public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; 90 public static final String PREF_DEBUG_SETTINGS = "debug_settings"; 91 public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal"; 92 93 // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead. 94 // This is being used only for the backward compatibility. 95 private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = 96 "pref_suppress_language_switch_key"; 97 98 private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN = 99 "pref_last_used_personalization_token"; 100 private static final String PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME = 101 "pref_last_used_personalization_dict_wiped_time"; 102 private static final String PREF_CORPUS_HANDLES_FOR_PERSONALIZATION = 103 "pref_corpus_handles_for_personalization"; 104 public static final String PREF_SEND_FEEDBACK = "send_feedback"; 105 public static final String PREF_ABOUT_KEYBOARD = "about_keyboard"; 106 107 // Emoji 108 public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys"; 109 public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id"; 110 public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id"; 111 112 private static final float UNDEFINED_PREFERENCE_VALUE_FLOAT = -1.0f; 113 private static final int UNDEFINED_PREFERENCE_VALUE_INT = -1; 114 115 private Context mContext; 116 private Resources mRes; 117 private SharedPreferences mPrefs; 118 private SettingsValues mSettingsValues; 119 private final ReentrantLock mSettingsValuesLock = new ReentrantLock(); 120 121 private static final Settings sInstance = new Settings(); 122 123 public static Settings getInstance() { 124 return sInstance; 125 } 126 127 public static void init(final Context context) { 128 sInstance.onCreate(context); 129 } 130 131 private Settings() { 132 // Intentional empty constructor for singleton. 133 } 134 135 private void onCreate(final Context context) { 136 mContext = context; 137 mRes = context.getResources(); 138 mPrefs = PreferenceManager.getDefaultSharedPreferences(context); 139 mPrefs.registerOnSharedPreferenceChangeListener(this); 140 } 141 142 public void onDestroy() { 143 mPrefs.unregisterOnSharedPreferenceChangeListener(this); 144 } 145 146 @Override 147 public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 148 mSettingsValuesLock.lock(); 149 try { 150 if (mSettingsValues == null) { 151 // TODO: Introduce a static function to register this class and ensure that 152 // loadSettings must be called before "onSharedPreferenceChanged" is called. 153 Log.w(TAG, "onSharedPreferenceChanged called before loadSettings."); 154 return; 155 } 156 loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes); 157 } finally { 158 mSettingsValuesLock.unlock(); 159 } 160 } 161 162 public void loadSettings(final Context context, final Locale locale, 163 final InputAttributes inputAttributes) { 164 mSettingsValuesLock.lock(); 165 mContext = context; 166 try { 167 final SharedPreferences prefs = mPrefs; 168 final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { 169 @Override 170 protected SettingsValues job(final Resources res) { 171 return new SettingsValues(context, prefs, res, inputAttributes); 172 } 173 }; 174 mSettingsValues = job.runInLocale(mRes, locale); 175 } finally { 176 mSettingsValuesLock.unlock(); 177 } 178 } 179 180 // TODO: Remove this method and add proxy method to SettingsValues. 181 public SettingsValues getCurrent() { 182 return mSettingsValues; 183 } 184 185 public boolean isInternal() { 186 return mSettingsValues.mIsInternal; 187 } 188 189 public boolean isWordSeparator(final int code) { 190 return mSettingsValues.isWordSeparator(code); 191 } 192 193 public boolean getBlockPotentiallyOffensive() { 194 return mSettingsValues.mBlockPotentiallyOffensive; 195 } 196 197 // Accessed from the settings interface, hence public 198 public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, 199 final Resources res) { 200 return prefs.getBoolean(Settings.PREF_SOUND_ON, 201 res.getBoolean(R.bool.config_default_sound_enabled)); 202 } 203 204 public static boolean readVibrationEnabled(final SharedPreferences prefs, 205 final Resources res) { 206 final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator(); 207 return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON, 208 res.getBoolean(R.bool.config_default_vibration_enabled)); 209 } 210 211 public static boolean readAutoCorrectEnabled(final String currentAutoCorrectionSetting, 212 final Resources res) { 213 final String autoCorrectionOff = res.getString( 214 R.string.auto_correction_threshold_mode_index_off); 215 return !currentAutoCorrectionSetting.equals(autoCorrectionOff); 216 } 217 218 public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs, 219 final Resources res) { 220 return prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, 221 res.getBoolean(R.bool.config_block_potentially_offensive)); 222 } 223 224 public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) { 225 return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config); 226 } 227 228 public static boolean readGestureInputEnabled(final SharedPreferences prefs, 229 final Resources res) { 230 return readFromBuildConfigIfGestureInputEnabled(res) 231 && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true); 232 } 233 234 public static boolean readPhraseGestureEnabled(final SharedPreferences prefs, 235 final Resources res) { 236 return prefs.getBoolean(Settings.PREF_PHRASE_GESTURE_ENABLED, 237 res.getBoolean(R.bool.config_default_phrase_gesture_enabled)); 238 } 239 240 public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) { 241 return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option); 242 } 243 244 public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, 245 final Resources res) { 246 final boolean defaultKeyPreviewPopup = res.getBoolean( 247 R.bool.config_default_key_preview_popup); 248 if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { 249 return defaultKeyPreviewPopup; 250 } 251 return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup); 252 } 253 254 public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs, 255 final Resources res) { 256 return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, 257 Integer.toString(res.getInteger( 258 R.integer.config_key_preview_linger_timeout)))); 259 } 260 261 public static boolean readShowsLanguageSwitchKey(final SharedPreferences prefs) { 262 if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) { 263 final boolean suppressLanguageSwitchKey = prefs.getBoolean( 264 PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false); 265 final SharedPreferences.Editor editor = prefs.edit(); 266 editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY); 267 editor.putBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey); 268 editor.apply(); 269 } 270 return prefs.getBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, true); 271 } 272 273 public static int readKeyboardThemeIndex(final SharedPreferences prefs, final Resources res) { 274 final int defaultThemeIndex = readDefaultKeyboardThemeIndex(res); 275 final String themeIndexString = prefs.getString(PREF_KEYBOARD_LAYOUT, null); 276 if (themeIndexString == null) { 277 return defaultThemeIndex; 278 } 279 try { 280 return Integer.parseInt(themeIndexString); 281 } catch (final NumberFormatException e) { 282 // Format error, returns default keyboard theme index. 283 Log.e(TAG, "Illegal keyboard theme in preference: " + themeIndexString + ", default to " 284 + defaultThemeIndex, e); 285 } 286 return defaultThemeIndex; 287 } 288 289 private static int readDefaultKeyboardThemeIndex(final Resources res) { 290 final String defaultThemeIndexString = res.getString( 291 R.string.config_default_keyboard_theme_index); 292 try { 293 return Integer.parseInt(defaultThemeIndexString); 294 } catch (final NumberFormatException e) { 295 final int defaultThemeIndex = KeyboardSwitcher.DEFAULT_THEME_INDEX; 296 Log.e(TAG, "Corrupted default keyoard theme in resource: " + defaultThemeIndexString 297 + ", default to " + defaultThemeIndex, e); 298 return defaultThemeIndex; 299 } 300 } 301 302 public static int resetAndGetDefaultKeyboardThemeIndex(final SharedPreferences prefs, 303 final Resources res) { 304 final int defaultThemeIndex = readDefaultKeyboardThemeIndex(res); 305 prefs.edit().putString(PREF_KEYBOARD_LAYOUT, Integer.toString(defaultThemeIndex)).apply(); 306 return defaultThemeIndex; 307 } 308 309 public static String readPrefAdditionalSubtypes(final SharedPreferences prefs, 310 final Resources res) { 311 final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes( 312 res.getStringArray(R.array.predefined_subtypes)); 313 return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes); 314 } 315 316 public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, 317 final String prefSubtypes) { 318 prefs.edit().putString(Settings.PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply(); 319 } 320 321 public static float readKeypressSoundVolume(final SharedPreferences prefs, 322 final Resources res) { 323 final float volume = prefs.getFloat( 324 PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); 325 return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume 326 : readDefaultKeypressSoundVolume(res); 327 } 328 329 // Default keypress sound volume for unknown devices. 330 // The negative value means system default. 331 private static final String DEFAULT_KEYPRESS_SOUND_VOLUME = Float.toString(-1.0f); 332 333 public static float readDefaultKeypressSoundVolume(final Resources res) { 334 return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(res, 335 R.array.keypress_volumes, DEFAULT_KEYPRESS_SOUND_VOLUME)); 336 } 337 338 public static int readKeyLongpressTimeout(final SharedPreferences prefs, 339 final Resources res) { 340 final int milliseconds = prefs.getInt( 341 PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); 342 return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds 343 : readDefaultKeyLongpressTimeout(res); 344 } 345 346 public static int readDefaultKeyLongpressTimeout(final Resources res) { 347 return res.getInteger(R.integer.config_default_longpress_key_timeout); 348 } 349 350 public static int readKeypressVibrationDuration(final SharedPreferences prefs, 351 final Resources res) { 352 final int milliseconds = prefs.getInt( 353 PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); 354 return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds 355 : readDefaultKeypressVibrationDuration(res); 356 } 357 358 // Default keypress vibration duration for unknown devices. 359 // The negative value means system default. 360 private static final String DEFAULT_KEYPRESS_VIBRATION_DURATION = Integer.toString(-1); 361 362 public static int readDefaultKeypressVibrationDuration(final Resources res) { 363 return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(res, 364 R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION)); 365 } 366 367 public static boolean readUsabilityStudyMode(final SharedPreferences prefs) { 368 return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); 369 } 370 371 public static float readKeyPreviewAnimationScale(final SharedPreferences prefs, 372 final String prefKey, final float defaultValue) { 373 final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); 374 return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue; 375 } 376 377 public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs, 378 final String prefKey, final int defaultValue) { 379 final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT); 380 return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; 381 } 382 383 public static boolean readUseFullscreenMode(final Resources res) { 384 return res.getBoolean(R.bool.config_use_fullscreen_mode); 385 } 386 387 public static boolean readShowSetupWizardIcon(final SharedPreferences prefs, 388 final Context context) { 389 final boolean enableSetupWizardByConfig = context.getResources().getBoolean( 390 R.bool.config_setup_wizard_available); 391 if (!enableSetupWizardByConfig) { 392 return false; 393 } 394 if (!prefs.contains(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { 395 final ApplicationInfo appInfo = context.getApplicationInfo(); 396 final boolean isApplicationInSystemImage = 397 (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 398 // Default value 399 return !isApplicationInSystemImage; 400 } 401 return prefs.getBoolean(Settings.PREF_SHOW_SETUP_WIZARD_ICON, false); 402 } 403 404 public static boolean isInternal(final SharedPreferences prefs) { 405 return prefs.getBoolean(Settings.PREF_KEY_IS_INTERNAL, false); 406 } 407 408 public void writeLastUsedPersonalizationToken(byte[] token) { 409 if (token == null) { 410 mPrefs.edit().remove(PREF_LAST_USED_PERSONALIZATION_TOKEN).apply(); 411 } else { 412 final String tokenStr = StringUtils.byteArrayToHexString(token); 413 mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply(); 414 } 415 } 416 417 public byte[] readLastUsedPersonalizationToken() { 418 final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null); 419 return StringUtils.hexStringToByteArray(tokenStr); 420 } 421 422 public void writeLastPersonalizationDictWipedTime(final long timestamp) { 423 mPrefs.edit().putLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, timestamp).apply(); 424 } 425 426 public long readLastPersonalizationDictGeneratedTime() { 427 return mPrefs.getLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, 0); 428 } 429 430 public void writeCorpusHandlesForPersonalization(final Set<String> corpusHandles) { 431 mPrefs.edit().putStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, corpusHandles).apply(); 432 } 433 434 public Set<String> readCorpusHandlesForPersonalization() { 435 final Set<String> emptySet = Collections.emptySet(); 436 return mPrefs.getStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, emptySet); 437 } 438 439 public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) { 440 prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply(); 441 } 442 443 public static String readEmojiRecentKeys(final SharedPreferences prefs) { 444 return prefs.getString(PREF_EMOJI_RECENT_KEYS, ""); 445 } 446 447 public static void writeLastTypedEmojiCategoryPageId( 448 final SharedPreferences prefs, final int categoryId, final int categoryPageId) { 449 final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId; 450 prefs.edit().putInt(key, categoryPageId).apply(); 451 } 452 453 public static int readLastTypedEmojiCategoryPageId( 454 final SharedPreferences prefs, final int categoryId) { 455 final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId; 456 return prefs.getInt(key, 0); 457 } 458 459 public static void writeLastShownEmojiCategoryId( 460 final SharedPreferences prefs, final int categoryId) { 461 prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply(); 462 } 463 464 public static int readLastShownEmojiCategoryId( 465 final SharedPreferences prefs, final int defValue) { 466 return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue); 467 } 468} 469