Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.content.res.Resources;
     22 import android.util.Log;
     23 import android.view.inputmethod.EditorInfo;
     24 import android.view.inputmethod.InputMethodSubtype;
     25 
     26 import com.android.inputmethod.keyboard.internal.KeySpecParser;
     27 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
     28 
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.HashMap;
     32 import java.util.Map;
     33 
     34 /**
     35  * When you call the constructor of this class, you may want to change the current system locale by
     36  * using {@link LocaleUtils.RunInLocale}.
     37  */
     38 public class SettingsValues {
     39     private static final String TAG = SettingsValues.class.getSimpleName();
     40 
     41     // From resources:
     42     public final int mDelayUpdateOldSuggestions;
     43     public final String mWeakSpaceStrippers;
     44     public final String mWeakSpaceSwappers;
     45     private final String mPhantomSpacePromotingSymbols;
     46     public final SuggestedWords mSuggestPuncList;
     47     private final String mSymbolsExcludedFromWordSeparators;
     48     public final String mWordSeparators;
     49     public final CharSequence mHintToSaveText;
     50 
     51     // From preferences, in the same order as xml/prefs.xml:
     52     public final boolean mAutoCap;
     53     public final boolean mVibrateOn;
     54     public final boolean mSoundOn;
     55     public final boolean mKeyPreviewPopupOn;
     56     private final String mVoiceMode;
     57     private final String mAutoCorrectionThresholdRawValue;
     58     public final String mShowSuggestionsSetting;
     59     @SuppressWarnings("unused") // TODO: Use this
     60     private final boolean mUsabilityStudyMode;
     61     public final boolean mIncludesOtherImesInLanguageSwitchList;
     62     public final boolean mIsLanguageSwitchKeySuppressed;
     63     @SuppressWarnings("unused") // TODO: Use this
     64     private final String mKeyPreviewPopupDismissDelayRawValue;
     65     public final boolean mUseContactsDict;
     66     // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
     67     public final boolean mBigramSuggestionEnabled;
     68     // Prediction: use bigrams to predict the next word when there is no input for it yet
     69     public final boolean mBigramPredictionEnabled;
     70     public final boolean mEnableSuggestionSpanInsertion;
     71     @SuppressWarnings("unused") // TODO: Use this
     72     private final int mVibrationDurationSettingsRawValue;
     73     @SuppressWarnings("unused") // TODO: Use this
     74     private final float mKeypressSoundVolumeRawValue;
     75     private final InputMethodSubtype[] mAdditionalSubtypes;
     76 
     77     // Deduced settings
     78     public final int mKeypressVibrationDuration;
     79     public final float mFxVolume;
     80     public final int mKeyPreviewPopupDismissDelay;
     81     public final boolean mAutoCorrectEnabled;
     82     public final float mAutoCorrectionThreshold;
     83     private final boolean mVoiceKeyEnabled;
     84     private final boolean mVoiceKeyOnMain;
     85 
     86     public SettingsValues(final SharedPreferences prefs, final Context context) {
     87         final Resources res = context.getResources();
     88 
     89         // Get the resources
     90         mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions);
     91         mWeakSpaceStrippers = res.getString(R.string.weak_space_stripping_symbols);
     92         mWeakSpaceSwappers = res.getString(R.string.weak_space_swapping_symbols);
     93         mPhantomSpacePromotingSymbols = res.getString(R.string.phantom_space_promoting_symbols);
     94         if (LatinImeLogger.sDBG) {
     95             final int length = mWeakSpaceStrippers.length();
     96             for (int i = 0; i < length; i = mWeakSpaceStrippers.offsetByCodePoints(i, 1)) {
     97                 if (isWeakSpaceSwapper(mWeakSpaceStrippers.codePointAt(i))) {
     98                     throw new RuntimeException("Char code " + mWeakSpaceStrippers.codePointAt(i)
     99                             + " is both a weak space swapper and stripper.");
    100                 }
    101             }
    102         }
    103         final String[] suggestPuncsSpec = KeySpecParser.parseCsvString(
    104                 res.getString(R.string.suggested_punctuations), null);
    105         mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
    106         mSymbolsExcludedFromWordSeparators =
    107                 res.getString(R.string.symbols_excluded_from_word_separators);
    108         mWordSeparators = createWordSeparators(mWeakSpaceStrippers, mWeakSpaceSwappers,
    109                 mSymbolsExcludedFromWordSeparators, res);
    110         mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
    111 
    112         // Get the settings preferences
    113         mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
    114         mVibrateOn = isVibrateOn(context, prefs, res);
    115         mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
    116                 res.getBoolean(R.bool.config_default_sound_enabled));
    117         mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
    118         final String voiceModeMain = res.getString(R.string.voice_mode_main);
    119         final String voiceModeOff = res.getString(R.string.voice_mode_off);
    120         mVoiceMode = prefs.getString(Settings.PREF_VOICE_MODE, voiceModeMain);
    121         mAutoCorrectionThresholdRawValue = prefs.getString(Settings.PREF_AUTO_CORRECTION_THRESHOLD,
    122                 res.getString(R.string.auto_correction_threshold_mode_index_modest));
    123         mShowSuggestionsSetting = prefs.getString(Settings.PREF_SHOW_SUGGESTIONS_SETTING,
    124                 res.getString(R.string.prefs_suggestion_visibility_default_value));
    125         mUsabilityStudyMode = getUsabilityStudyMode(prefs);
    126         mIncludesOtherImesInLanguageSwitchList = prefs.getBoolean(
    127                 Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false);
    128         mIsLanguageSwitchKeySuppressed = isLanguageSwitchKeySupressed(prefs);
    129         mKeyPreviewPopupDismissDelayRawValue = prefs.getString(
    130                 Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
    131                 Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout)));
    132         mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
    133         mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
    134         mBigramSuggestionEnabled = mAutoCorrectEnabled
    135                 && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
    136         mBigramPredictionEnabled = mBigramSuggestionEnabled
    137                 && isBigramPredictionEnabled(prefs, res);
    138         // TODO: remove mEnableSuggestionSpanInsertion. It's always true.
    139         mEnableSuggestionSpanInsertion = true;
    140         mVibrationDurationSettingsRawValue =
    141                 prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
    142         mKeypressSoundVolumeRawValue = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
    143 
    144         // Compute other readable settings
    145         mKeypressVibrationDuration = getCurrentVibrationDuration(prefs, res);
    146         mFxVolume = getCurrentKeypressSoundVolume(prefs, res);
    147         mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res);
    148         mAutoCorrectionThreshold = getAutoCorrectionThreshold(res,
    149                 mAutoCorrectionThresholdRawValue);
    150         mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff);
    151         mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
    152         mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray(
    153                 getPrefAdditionalSubtypes(prefs, res));
    154     }
    155 
    156     // Helper functions to create member values.
    157     private static SuggestedWords createSuggestPuncList(final String[] puncs) {
    158         final ArrayList<SuggestedWords.SuggestedWordInfo> puncList =
    159                 new ArrayList<SuggestedWords.SuggestedWordInfo>();
    160         if (puncs != null) {
    161             for (final String puncSpec : puncs) {
    162                 puncList.add(new SuggestedWords.SuggestedWordInfo(
    163                         KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE));
    164             }
    165         }
    166         return new SuggestedWords(puncList,
    167                 false /* typedWordValid */,
    168                 false /* hasAutoCorrectionCandidate */,
    169                 false /* allowsToBeAutoCorrected */,
    170                 true /* isPunctuationSuggestions */,
    171                 false /* isObsoleteSuggestions */,
    172                 false /* isPrediction */);
    173     }
    174 
    175     private static String createWordSeparators(final String weakSpaceStrippers,
    176             final String weakSpaceSwappers, final String symbolsExcludedFromWordSeparators,
    177             final Resources res) {
    178         String wordSeparators = weakSpaceStrippers + weakSpaceSwappers
    179                 + res.getString(R.string.phantom_space_promoting_symbols);
    180         for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) {
    181             wordSeparators = wordSeparators.replace(
    182                     symbolsExcludedFromWordSeparators.substring(i, i + 1), "");
    183         }
    184         return wordSeparators;
    185     }
    186 
    187     private static boolean isVibrateOn(final Context context, final SharedPreferences prefs,
    188             final Resources res) {
    189         final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator();
    190         return hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON,
    191                 res.getBoolean(R.bool.config_default_vibration_enabled));
    192     }
    193 
    194     public boolean isWordSeparator(int code) {
    195         return mWordSeparators.contains(String.valueOf((char)code));
    196     }
    197 
    198     public boolean isSymbolExcludedFromWordSeparators(int code) {
    199         return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
    200     }
    201 
    202     public boolean isWeakSpaceStripper(int code) {
    203         // TODO: this does not work if the code does not fit in a char
    204         return mWeakSpaceStrippers.contains(String.valueOf((char)code));
    205     }
    206 
    207     public boolean isWeakSpaceSwapper(int code) {
    208         // TODO: this does not work if the code does not fit in a char
    209         return mWeakSpaceSwappers.contains(String.valueOf((char)code));
    210     }
    211 
    212     public boolean isPhantomSpacePromotingSymbol(int code) {
    213         // TODO: this does not work if the code does not fit in a char
    214         return mPhantomSpacePromotingSymbols.contains(String.valueOf((char)code));
    215     }
    216 
    217     private static boolean isAutoCorrectEnabled(final Resources resources,
    218             final String currentAutoCorrectionSetting) {
    219         final String autoCorrectionOff = resources.getString(
    220                 R.string.auto_correction_threshold_mode_index_off);
    221         return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
    222     }
    223 
    224     // Public to access from KeyboardSwitcher. Should it have access to some
    225     // process-global instance instead?
    226     public static boolean isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources) {
    227         final boolean showPopupOption = resources.getBoolean(
    228                 R.bool.config_enable_show_popup_on_keypress_option);
    229         if (!showPopupOption) return resources.getBoolean(R.bool.config_default_popup_preview);
    230         return sp.getBoolean(Settings.PREF_POPUP_ON,
    231                 resources.getBoolean(R.bool.config_default_popup_preview));
    232     }
    233 
    234     // Likewise
    235     public static int getKeyPreviewPopupDismissDelay(SharedPreferences sp,
    236             Resources resources) {
    237         // TODO: use mKeyPreviewPopupDismissDelayRawValue instead of reading it again here.
    238         return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
    239                 Integer.toString(resources.getInteger(
    240                         R.integer.config_key_preview_linger_timeout))));
    241     }
    242 
    243     private static boolean isBigramSuggestionEnabled(final SharedPreferences sp,
    244             final Resources resources, final boolean autoCorrectEnabled) {
    245         // TODO: remove this method. Bigram suggestion is always true.
    246         return true;
    247     }
    248 
    249     private static boolean isBigramPredictionEnabled(final SharedPreferences sp,
    250             final Resources resources) {
    251         return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean(
    252                 R.bool.config_default_next_word_prediction));
    253     }
    254 
    255     private static float getAutoCorrectionThreshold(final Resources resources,
    256             final String currentAutoCorrectionSetting) {
    257         final String[] autoCorrectionThresholdValues = resources.getStringArray(
    258                 R.array.auto_correction_threshold_values);
    259         // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
    260         float autoCorrectionThreshold = Float.MAX_VALUE;
    261         try {
    262             final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
    263             if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
    264                 autoCorrectionThreshold = Float.parseFloat(
    265                         autoCorrectionThresholdValues[arrayIndex]);
    266             }
    267         } catch (NumberFormatException e) {
    268             // Whenever the threshold settings are correct, never come here.
    269             autoCorrectionThreshold = Float.MAX_VALUE;
    270             Log.w(TAG, "Cannot load auto correction threshold setting."
    271                     + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
    272                     + ", autoCorrectionThresholdValues: "
    273                     + Arrays.toString(autoCorrectionThresholdValues));
    274         }
    275         return autoCorrectionThreshold;
    276     }
    277 
    278     public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) {
    279         final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
    280         final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
    281         return shortcutImeEnabled && mVoiceKeyEnabled
    282                 && !InputTypeUtils.isPasswordInputType(inputType);
    283     }
    284 
    285     public boolean isVoiceKeyOnMain() {
    286         return mVoiceKeyOnMain;
    287     }
    288 
    289     public static boolean isLanguageSwitchKeySupressed(SharedPreferences sp) {
    290         return sp.getBoolean(Settings.PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
    291     }
    292 
    293     public boolean isLanguageSwitchKeyEnabled(Context context) {
    294         if (mIsLanguageSwitchKeySuppressed) {
    295             return false;
    296         }
    297         if (mIncludesOtherImesInLanguageSwitchList) {
    298             return ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
    299                     context, /* include aux subtypes */false);
    300         } else {
    301             return ImfUtils.hasMultipleEnabledSubtypesInThisIme(
    302                     context, /* include aux subtypes */false);
    303         }
    304     }
    305 
    306     public boolean isFullscreenModeAllowed(Resources res) {
    307         return res.getBoolean(R.bool.config_use_fullscreen_mode);
    308     }
    309 
    310     public InputMethodSubtype[] getAdditionalSubtypes() {
    311         return mAdditionalSubtypes;
    312     }
    313 
    314     public static String getPrefAdditionalSubtypes(final SharedPreferences prefs,
    315             final Resources res) {
    316         final String prefSubtypes = res.getString(R.string.predefined_subtypes, "");
    317         return prefs.getString(Settings.PREF_CUSTOM_INPUT_STYLES, prefSubtypes);
    318     }
    319 
    320     // Accessed from the settings interface, hence public
    321     public static float getCurrentKeypressSoundVolume(final SharedPreferences sp,
    322                 final Resources res) {
    323         // TODO: use mVibrationDurationSettingsRawValue instead of reading it again here
    324         final float volume = sp.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
    325         if (volume >= 0) {
    326             return volume;
    327         }
    328 
    329         return Float.parseFloat(
    330                 Utils.getDeviceOverrideValue(res, R.array.keypress_volumes, "-1.0f"));
    331     }
    332 
    333     // Likewise
    334     public static int getCurrentVibrationDuration(final SharedPreferences sp,
    335                 final Resources res) {
    336         // TODO: use mKeypressVibrationDuration instead of reading it again here
    337         final int ms = sp.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
    338         if (ms >= 0) {
    339             return ms;
    340         }
    341 
    342         return Integer.parseInt(
    343                 Utils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations, "-1"));
    344     }
    345 
    346     // Likewise
    347     public static boolean getUsabilityStudyMode(final SharedPreferences prefs) {
    348         // TODO: use mUsabilityStudyMode instead of reading it again here
    349         return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true);
    350     }
    351 
    352     public static long getLastUserHistoryWriteTime(
    353             final SharedPreferences prefs, final String locale) {
    354         final String str = prefs.getString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
    355         final HashMap<String, Long> map = Utils.localeAndTimeStrToHashMap(str);
    356         if (map.containsKey(locale)) {
    357             return map.get(locale);
    358         }
    359         return 0;
    360     }
    361 
    362     public static void setLastUserHistoryWriteTime(
    363             final SharedPreferences prefs, final String locale) {
    364         final String oldStr = prefs.getString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
    365         final HashMap<String, Long> map = Utils.localeAndTimeStrToHashMap(oldStr);
    366         map.put(locale, System.currentTimeMillis());
    367         final String newStr = Utils.localeAndTimeHashMapToStr(map);
    368         prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
    369     }
    370 }
    371