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