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