• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SettingsValues(final SharedPreferences prefs, final Context context)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.
createSuggestPuncList(final String[] puncs)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 
createWordSeparators(final String weakSpaceStrippers, final String weakSpaceSwappers, final String symbolsExcludedFromWordSeparators, final Resources res)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 
isVibrateOn(final Context context, final SharedPreferences prefs, final Resources res)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 
isWordSeparator(int code)194     public boolean isWordSeparator(int code) {
195         return mWordSeparators.contains(String.valueOf((char)code));
196     }
197 
isSymbolExcludedFromWordSeparators(int code)198     public boolean isSymbolExcludedFromWordSeparators(int code) {
199         return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
200     }
201 
isWeakSpaceStripper(int code)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 
isWeakSpaceSwapper(int code)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 
isPhantomSpacePromotingSymbol(int code)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 
isAutoCorrectEnabled(final Resources resources, final String currentAutoCorrectionSetting)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?
isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources)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
getKeyPreviewPopupDismissDelay(SharedPreferences sp, Resources resources)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 
isBigramSuggestionEnabled(final SharedPreferences sp, final Resources resources, final boolean autoCorrectEnabled)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 
isBigramPredictionEnabled(final SharedPreferences sp, final Resources resources)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 
getAutoCorrectionThreshold(final Resources resources, final String currentAutoCorrectionSetting)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 
isVoiceKeyEnabled(final EditorInfo editorInfo)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 
isVoiceKeyOnMain()285     public boolean isVoiceKeyOnMain() {
286         return mVoiceKeyOnMain;
287     }
288 
isLanguageSwitchKeySupressed(SharedPreferences sp)289     public static boolean isLanguageSwitchKeySupressed(SharedPreferences sp) {
290         return sp.getBoolean(Settings.PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
291     }
292 
isLanguageSwitchKeyEnabled(Context context)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 
isFullscreenModeAllowed(Resources res)306     public boolean isFullscreenModeAllowed(Resources res) {
307         return res.getBoolean(R.bool.config_use_fullscreen_mode);
308     }
309 
getAdditionalSubtypes()310     public InputMethodSubtype[] getAdditionalSubtypes() {
311         return mAdditionalSubtypes;
312     }
313 
getPrefAdditionalSubtypes(final SharedPreferences prefs, final Resources res)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
getCurrentKeypressSoundVolume(final SharedPreferences sp, final Resources res)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
getCurrentVibrationDuration(final SharedPreferences sp, final Resources res)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
getUsabilityStudyMode(final SharedPreferences prefs)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 
getLastUserHistoryWriteTime( final SharedPreferences prefs, final String locale)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 
setLastUserHistoryWriteTime( final SharedPreferences prefs, final String locale)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