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