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 17 package com.android.inputmethod.latin.settings; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.content.pm.ApplicationInfo; 22 import android.content.res.Resources; 23 import android.preference.PreferenceManager; 24 import android.util.Log; 25 26 import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; 27 import com.android.inputmethod.latin.InputAttributes; 28 import com.android.inputmethod.latin.R; 29 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; 30 import com.android.inputmethod.latin.utils.LocaleUtils; 31 import com.android.inputmethod.latin.utils.ResourceUtils; 32 import com.android.inputmethod.latin.utils.RunInLocale; 33 import com.android.inputmethod.latin.utils.StringUtils; 34 35 import java.util.HashMap; 36 import java.util.Locale; 37 import java.util.concurrent.locks.ReentrantLock; 38 39 public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { 40 private static final String TAG = Settings.class.getSimpleName(); 41 // In the same order as xml/prefs.xml 42 public static final String PREF_GENERAL_SETTINGS = "general_settings"; 43 public static final String PREF_AUTO_CAP = "auto_cap"; 44 public static final String PREF_VIBRATE_ON = "vibrate_on"; 45 public static final String PREF_SOUND_ON = "sound_on"; 46 public static final String PREF_POPUP_ON = "popup_on"; 47 // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead. 48 public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode"; 49 public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key"; 50 public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; 51 public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary"; 52 public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; 53 public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; 54 public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; 55 public static final String PREF_MISC_SETTINGS = "misc_settings"; 56 public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = 57 "last_user_dictionary_write_time"; 58 public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; 59 public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; 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_CUSTOM_INPUT_STYLES = "custom_input_styles"; 69 public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = 70 "pref_key_preview_popup_dismiss_delay"; 71 public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; 72 public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings"; 73 public static final String PREF_GESTURE_INPUT = "gesture_input"; 74 public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview"; 75 public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; 76 public static final String PREF_VIBRATION_DURATION_SETTINGS = 77 "pref_vibration_duration_settings"; 78 public static final String PREF_KEYPRESS_SOUND_VOLUME = 79 "pref_keypress_sound_volume"; 80 public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail"; 81 public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT = 82 "pref_gesture_floating_preview_text"; 83 public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon"; 84 public static final String PREF_PHRASE_GESTURE_ENABLED = "pref_gesture_space_aware"; 85 86 public static final String PREF_INPUT_LANGUAGE = "input_language"; 87 public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; 88 public static final String PREF_DEBUG_SETTINGS = "debug_settings"; 89 public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal"; 90 91 // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead. 92 // This is being used only for the backward compatibility. 93 private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = 94 "pref_suppress_language_switch_key"; 95 96 private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN = 97 "pref_last_used_personalization_token"; 98 public static final String PREF_SEND_FEEDBACK = "send_feedback"; 99 public static final String PREF_ABOUT_KEYBOARD = "about_keyboard"; 100 101 // Emoji 102 public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys"; 103 public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id"; 104 public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id"; 105 106 private Resources mRes; 107 private SharedPreferences mPrefs; 108 private SettingsValues mSettingsValues; 109 private final ReentrantLock mSettingsValuesLock = new ReentrantLock(); 110 111 private static final Settings sInstance = new Settings(); 112 113 public static Settings getInstance() { 114 return sInstance; 115 } 116 117 public static void init(final Context context) { 118 sInstance.onCreate(context); 119 } 120 121 private Settings() { 122 // Intentional empty constructor for singleton. 123 } 124 125 private void onCreate(final Context context) { 126 mRes = context.getResources(); 127 mPrefs = PreferenceManager.getDefaultSharedPreferences(context); 128 mPrefs.registerOnSharedPreferenceChangeListener(this); 129 } 130 131 public void onDestroy() { 132 mPrefs.unregisterOnSharedPreferenceChangeListener(this); 133 } 134 135 @Override 136 public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 137 mSettingsValuesLock.lock(); 138 try { 139 if (mSettingsValues == null) { 140 // TODO: Introduce a static function to register this class and ensure that 141 // loadSettings must be called before "onSharedPreferenceChanged" is called. 142 Log.w(TAG, "onSharedPreferenceChanged called before loadSettings."); 143 return; 144 } 145 loadSettings(mSettingsValues.mLocale, mSettingsValues.mInputAttributes); 146 } finally { 147 mSettingsValuesLock.unlock(); 148 } 149 } 150 151 public void loadSettings(final Locale locale, final InputAttributes inputAttributes) { 152 mSettingsValuesLock.lock(); 153 try { 154 final SharedPreferences prefs = mPrefs; 155 final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { 156 @Override 157 protected SettingsValues job(final Resources res) { 158 return new SettingsValues(prefs, locale, res, inputAttributes); 159 } 160 }; 161 mSettingsValues = job.runInLocale(mRes, locale); 162 } finally { 163 mSettingsValuesLock.unlock(); 164 } 165 } 166 167 // TODO: Remove this method and add proxy method to SettingsValues. 168 public SettingsValues getCurrent() { 169 return mSettingsValues; 170 } 171 172 public boolean isInternal() { 173 return mSettingsValues.mIsInternal; 174 } 175 176 public String getWordSeparators() { 177 return mSettingsValues.mWordSeparators; 178 } 179 180 public boolean isWordSeparator(final int code) { 181 return mSettingsValues.isWordSeparator(code); 182 } 183 184 public boolean getBlockPotentiallyOffensive() { 185 return mSettingsValues.mBlockPotentiallyOffensive; 186 } 187 188 // Accessed from the settings interface, hence public 189 public static boolean readKeypressSoundEnabled(final SharedPreferences prefs, 190 final Resources res) { 191 return prefs.getBoolean(Settings.PREF_SOUND_ON, 192 res.getBoolean(R.bool.config_default_sound_enabled)); 193 } 194 195 public static boolean readVibrationEnabled(final SharedPreferences prefs, 196 final Resources res) { 197 final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator(); 198 return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON, 199 res.getBoolean(R.bool.config_default_vibration_enabled)); 200 } 201 202 public static boolean readAutoCorrectEnabled(final String currentAutoCorrectionSetting, 203 final Resources res) { 204 final String autoCorrectionOff = res.getString( 205 R.string.auto_correction_threshold_mode_index_off); 206 return !currentAutoCorrectionSetting.equals(autoCorrectionOff); 207 } 208 209 public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs, 210 final Resources res) { 211 return prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, 212 res.getBoolean(R.bool.config_block_potentially_offensive)); 213 } 214 215 public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) { 216 return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config); 217 } 218 219 public static boolean readGestureInputEnabled(final SharedPreferences prefs, 220 final Resources res) { 221 return readFromBuildConfigIfGestureInputEnabled(res) 222 && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true); 223 } 224 225 public static boolean readPhraseGestureEnabled(final SharedPreferences prefs, 226 final Resources res) { 227 return prefs.getBoolean(Settings.PREF_PHRASE_GESTURE_ENABLED, 228 res.getBoolean(R.bool.config_default_phrase_gesture_enabled)); 229 } 230 231 public static boolean readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption( 232 final Resources res) { 233 return res.getBoolean(R.bool.config_enable_show_option_of_key_preview_popup); 234 } 235 236 public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, 237 final Resources res) { 238 final boolean defaultKeyPreviewPopup = res.getBoolean( 239 R.bool.config_default_key_preview_popup); 240 if (!readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) { 241 return defaultKeyPreviewPopup; 242 } 243 return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup); 244 } 245 246 public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs, 247 final Resources res) { 248 return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, 249 Integer.toString(res.getInteger( 250 R.integer.config_key_preview_linger_timeout)))); 251 } 252 253 public static boolean readShowsLanguageSwitchKey(final SharedPreferences prefs) { 254 if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) { 255 final boolean suppressLanguageSwitchKey = prefs.getBoolean( 256 PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false); 257 final SharedPreferences.Editor editor = prefs.edit(); 258 editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY); 259 editor.putBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey); 260 editor.apply(); 261 } 262 return prefs.getBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, true); 263 } 264 265 public static String readPrefAdditionalSubtypes(final SharedPreferences prefs, 266 final Resources res) { 267 final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes( 268 res.getStringArray(R.array.predefined_subtypes)); 269 return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes); 270 } 271 272 public static void writePrefAdditionalSubtypes(final SharedPreferences prefs, 273 final String prefSubtypes) { 274 prefs.edit().putString(Settings.PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply(); 275 } 276 277 public static float readKeypressSoundVolume(final SharedPreferences prefs, 278 final Resources res) { 279 final float volume = prefs.getFloat(PREF_KEYPRESS_SOUND_VOLUME, -1.0f); 280 return (volume >= 0) ? volume : readDefaultKeypressSoundVolume(res); 281 } 282 283 public static float readDefaultKeypressSoundVolume(final Resources res) { 284 return Float.parseFloat( 285 ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes)); 286 } 287 288 public static int readKeyLongpressTimeout(final SharedPreferences prefs, 289 final Resources res) { 290 final int ms = prefs.getInt(PREF_KEY_LONGPRESS_TIMEOUT, -1); 291 return (ms >= 0) ? ms : readDefaultKeyLongpressTimeout(res); 292 } 293 294 public static int readDefaultKeyLongpressTimeout(final Resources res) { 295 return res.getInteger(R.integer.config_default_longpress_key_timeout); 296 } 297 298 public static int readKeypressVibrationDuration(final SharedPreferences prefs, 299 final Resources res) { 300 final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1); 301 return (ms >= 0) ? ms : readDefaultKeypressVibrationDuration(res); 302 } 303 304 public static int readDefaultKeypressVibrationDuration(final Resources res) { 305 return Integer.parseInt( 306 ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations)); 307 } 308 309 public static boolean readUsabilityStudyMode(final SharedPreferences prefs) { 310 return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); 311 } 312 313 public static long readLastUserHistoryWriteTime(final SharedPreferences prefs, 314 final String locale) { 315 final String str = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, ""); 316 final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str); 317 if (map.containsKey(locale)) { 318 return map.get(locale); 319 } 320 return 0; 321 } 322 323 public static void writeLastUserHistoryWriteTime(final SharedPreferences prefs, 324 final String locale) { 325 final String oldStr = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, ""); 326 final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr); 327 map.put(locale, System.currentTimeMillis()); 328 final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map); 329 prefs.edit().putString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply(); 330 } 331 332 public static boolean readUseFullscreenMode(final Resources res) { 333 return res.getBoolean(R.bool.config_use_fullscreen_mode); 334 } 335 336 public static boolean readShowSetupWizardIcon(final SharedPreferences prefs, 337 final Context context) { 338 final boolean enableSetupWizardByConfig = context.getResources().getBoolean( 339 R.bool.config_setup_wizard_available); 340 if (!enableSetupWizardByConfig) { 341 return false; 342 } 343 if (!prefs.contains(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { 344 final ApplicationInfo appInfo = context.getApplicationInfo(); 345 final boolean isApplicationInSystemImage = 346 (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 347 // Default value 348 return !isApplicationInSystemImage; 349 } 350 return prefs.getBoolean(Settings.PREF_SHOW_SETUP_WIZARD_ICON, false); 351 } 352 353 public static boolean isInternal(final SharedPreferences prefs) { 354 return prefs.getBoolean(Settings.PREF_KEY_IS_INTERNAL, false); 355 } 356 357 public static boolean readUseOnlyPersonalizationDictionaryForDebug( 358 final SharedPreferences prefs) { 359 return prefs.getBoolean( 360 DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false); 361 } 362 363 public static boolean readBoostPersonalizationDictionaryForDebug( 364 final SharedPreferences prefs) { 365 return prefs.getBoolean( 366 DebugSettings.PREF_BOOST_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false); 367 } 368 369 public void writeLastUsedPersonalizationToken(byte[] token) { 370 final String tokenStr = StringUtils.byteArrayToHexString(token); 371 mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply(); 372 } 373 374 public byte[] readLastUsedPersonalizationToken() { 375 final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null); 376 return StringUtils.hexStringToByteArray(tokenStr); 377 } 378 379 public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) { 380 prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply(); 381 } 382 383 public static String readEmojiRecentKeys(final SharedPreferences prefs) { 384 return prefs.getString(PREF_EMOJI_RECENT_KEYS, ""); 385 } 386 387 public static void writeLastTypedEmojiCategoryPageId( 388 final SharedPreferences prefs, final int categoryId, final int categoryPageId) { 389 final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId; 390 prefs.edit().putInt(key, categoryPageId).apply(); 391 } 392 393 public static int readLastTypedEmojiCategoryPageId( 394 final SharedPreferences prefs, final int categoryId) { 395 final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId; 396 return prefs.getInt(key, 0); 397 } 398 399 public static void writeLastShownEmojiCategoryId( 400 final SharedPreferences prefs, final int categoryId) { 401 prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply(); 402 } 403 404 public static int readLastShownEmojiCategoryId( 405 final SharedPreferences prefs, final int defValue) { 406 return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue); 407 } 408 } 409