1 /* 2 * Copyright (C) 2011 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; 18 19 import android.content.SharedPreferences; 20 import android.content.res.Configuration; 21 import android.content.res.Resources; 22 import android.util.Log; 23 import android.view.inputmethod.EditorInfo; 24 25 import com.android.inputmethod.keyboard.internal.KeySpecParser; 26 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 31 /** 32 * When you call the constructor of this class, you may want to change the current system locale by 33 * using {@link LocaleUtils.RunInLocale}. 34 */ 35 public final class SettingsValues { 36 private static final String TAG = SettingsValues.class.getSimpleName(); 37 // "floatNegativeInfinity" is a special marker string for Float.NEGATIVE_INFINITE 38 // currently used for auto-correction 39 private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity"; 40 41 // From resources: 42 public final int mDelayUpdateOldSuggestions; 43 public final int[] mSymbolsPrecededBySpace; 44 public final int[] mSymbolsFollowedBySpace; 45 public final int[] mWordConnectors; 46 public final SuggestedWords mSuggestPuncList; 47 public final String mWordSeparators; 48 public final CharSequence mHintToSaveText; 49 50 // From preferences, in the same order as xml/prefs.xml: 51 public final boolean mAutoCap; 52 public final boolean mVibrateOn; 53 public final boolean mSoundOn; 54 public final boolean mKeyPreviewPopupOn; 55 private final String mVoiceMode; 56 public final boolean mIncludesOtherImesInLanguageSwitchList; 57 public final boolean mShowsLanguageSwitchKey; 58 public final boolean mUseContactsDict; 59 public final boolean mUseDoubleSpacePeriod; 60 public final boolean mBlockPotentiallyOffensive; 61 // Use bigrams to predict the next word when there is no input for it yet 62 public final boolean mBigramPredictionEnabled; 63 public final boolean mGestureInputEnabled; 64 public final boolean mGesturePreviewTrailEnabled; 65 public final boolean mGestureFloatingPreviewTextEnabled; 66 public final boolean mSlidingKeyInputPreviewEnabled; 67 public final int mKeyLongpressTimeout; 68 69 // From the input box 70 public final InputAttributes mInputAttributes; 71 72 // Deduced settings 73 public final int mKeypressVibrationDuration; 74 public final float mKeypressSoundVolume; 75 public final int mKeyPreviewPopupDismissDelay; 76 private final boolean mAutoCorrectEnabled; 77 public final float mAutoCorrectionThreshold; 78 public final boolean mCorrectionEnabled; 79 public final int mSuggestionVisibility; 80 private final boolean mVoiceKeyEnabled; 81 private final boolean mVoiceKeyOnMain; 82 83 // Debug settings 84 public final boolean mIsInternal; 85 86 public SettingsValues(final SharedPreferences prefs, final Resources res, 87 final InputAttributes inputAttributes) { 88 // Get the resources 89 mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); 90 mSymbolsPrecededBySpace = 91 StringUtils.toCodePointArray(res.getString(R.string.symbols_preceded_by_space)); 92 Arrays.sort(mSymbolsPrecededBySpace); 93 mSymbolsFollowedBySpace = 94 StringUtils.toCodePointArray(res.getString(R.string.symbols_followed_by_space)); 95 Arrays.sort(mSymbolsFollowedBySpace); 96 mWordConnectors = 97 StringUtils.toCodePointArray(res.getString(R.string.symbols_word_connectors)); 98 Arrays.sort(mWordConnectors); 99 final String[] suggestPuncsSpec = StringUtils.parseCsvString(res.getString( 100 R.string.suggested_punctuations)); 101 mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec); 102 mWordSeparators = res.getString(R.string.symbols_word_separators); 103 mHintToSaveText = res.getText(R.string.hint_add_to_dictionary); 104 105 // Store the input attributes 106 if (null == inputAttributes) { 107 mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); 108 } else { 109 mInputAttributes = inputAttributes; 110 } 111 112 // Get the settings preferences 113 mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); 114 mVibrateOn = Settings.readVibrationEnabled(prefs, res); 115 mSoundOn = Settings.readKeypressSoundEnabled(prefs, res); 116 mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); 117 mSlidingKeyInputPreviewEnabled = prefs.getBoolean( 118 Settings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); 119 final String voiceModeMain = res.getString(R.string.voice_mode_main); 120 final String voiceModeOff = res.getString(R.string.voice_mode_off); 121 mVoiceMode = prefs.getString(Settings.PREF_VOICE_MODE, voiceModeMain); 122 final String autoCorrectionThresholdRawValue = prefs.getString( 123 Settings.PREF_AUTO_CORRECTION_THRESHOLD, 124 res.getString(R.string.auto_correction_threshold_mode_index_modest)); 125 mIncludesOtherImesInLanguageSwitchList = prefs.getBoolean( 126 Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false); 127 mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs); 128 mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); 129 mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true); 130 mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res); 131 mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res); 132 mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); 133 134 // Compute other readable settings 135 mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res); 136 mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); 137 mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res); 138 mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res); 139 mAutoCorrectionThreshold = readAutoCorrectionThreshold(res, 140 autoCorrectionThresholdRawValue); 141 mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); 142 mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); 143 mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); 144 mGesturePreviewTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); 145 mGestureFloatingPreviewTextEnabled = prefs.getBoolean( 146 Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); 147 mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; 148 final String showSuggestionsSetting = prefs.getString( 149 Settings.PREF_SHOW_SUGGESTIONS_SETTING, 150 res.getString(R.string.prefs_suggestion_visibility_default_value)); 151 mSuggestionVisibility = createSuggestionVisibility(res, showSuggestionsSetting); 152 mIsInternal = Settings.isInternal(prefs); 153 } 154 155 public boolean isApplicationSpecifiedCompletionsOn() { 156 return mInputAttributes.mApplicationSpecifiedCompletionOn; 157 } 158 159 public boolean isSuggestionsRequested(final int displayOrientation) { 160 return mInputAttributes.mIsSettingsSuggestionStripOn 161 && (mCorrectionEnabled 162 || isSuggestionStripVisibleInOrientation(displayOrientation)); 163 } 164 165 public boolean isSuggestionStripVisibleInOrientation(final int orientation) { 166 return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE) 167 || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE 168 && orientation == Configuration.ORIENTATION_PORTRAIT); 169 } 170 171 public boolean isWordSeparator(final int code) { 172 return mWordSeparators.contains(String.valueOf((char)code)); 173 } 174 175 public boolean isWordConnector(final int code) { 176 return Arrays.binarySearch(mWordConnectors, code) >= 0; 177 } 178 179 public boolean isUsuallyPrecededBySpace(final int code) { 180 return Arrays.binarySearch(mSymbolsPrecededBySpace, code) >= 0; 181 } 182 183 public boolean isUsuallyFollowedBySpace(final int code) { 184 return Arrays.binarySearch(mSymbolsFollowedBySpace, code) >= 0; 185 } 186 187 public boolean shouldInsertSpacesAutomatically() { 188 return mInputAttributes.mShouldInsertSpacesAutomatically; 189 } 190 191 public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) { 192 final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); 193 final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; 194 return shortcutImeEnabled && mVoiceKeyEnabled 195 && !InputTypeUtils.isPasswordInputType(inputType); 196 } 197 198 public boolean isVoiceKeyOnMain() { 199 return mVoiceKeyOnMain; 200 } 201 202 public boolean isLanguageSwitchKeyEnabled() { 203 if (!mShowsLanguageSwitchKey) { 204 return false; 205 } 206 final RichInputMethodManager imm = RichInputMethodManager.getInstance(); 207 if (mIncludesOtherImesInLanguageSwitchList) { 208 return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */); 209 } else { 210 return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */); 211 } 212 } 213 214 public boolean isSameInputType(final EditorInfo editorInfo) { 215 return mInputAttributes.isSameInputType(editorInfo); 216 } 217 218 // Helper functions to create member values. 219 private static SuggestedWords createSuggestPuncList(final String[] puncs) { 220 final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList(); 221 if (puncs != null) { 222 for (final String puncSpec : puncs) { 223 // TODO: Stop using KeySpceParser.getLabel(). 224 puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec), 225 SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED, 226 Dictionary.TYPE_HARDCODED)); 227 } 228 } 229 return new SuggestedWords(puncList, 230 false /* typedWordValid */, 231 false /* hasAutoCorrectionCandidate */, 232 true /* isPunctuationSuggestions */, 233 false /* isObsoleteSuggestions */, 234 false /* isPrediction */); 235 } 236 237 private static final int SUGGESTION_VISIBILITY_SHOW_VALUE = 238 R.string.prefs_suggestion_visibility_show_value; 239 private static final int SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE = 240 R.string.prefs_suggestion_visibility_show_only_portrait_value; 241 private static final int SUGGESTION_VISIBILITY_HIDE_VALUE = 242 R.string.prefs_suggestion_visibility_hide_value; 243 private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] { 244 SUGGESTION_VISIBILITY_SHOW_VALUE, 245 SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE, 246 SUGGESTION_VISIBILITY_HIDE_VALUE 247 }; 248 249 private static int createSuggestionVisibility(final Resources res, 250 final String suggestionVisiblityStr) { 251 for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) { 252 if (suggestionVisiblityStr.equals(res.getString(visibility))) { 253 return visibility; 254 } 255 } 256 throw new RuntimeException("Bug: visibility string is not configured correctly"); 257 } 258 259 private static boolean readBigramPredictionEnabled(final SharedPreferences prefs, 260 final Resources res) { 261 return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean( 262 R.bool.config_default_next_word_prediction)); 263 } 264 265 private static float readAutoCorrectionThreshold(final Resources res, 266 final String currentAutoCorrectionSetting) { 267 final String[] autoCorrectionThresholdValues = res.getStringArray( 268 R.array.auto_correction_threshold_values); 269 // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off. 270 float autoCorrectionThreshold = Float.MAX_VALUE; 271 try { 272 final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting); 273 if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) { 274 final String val = autoCorrectionThresholdValues[arrayIndex]; 275 if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) { 276 autoCorrectionThreshold = Float.NEGATIVE_INFINITY; 277 } else { 278 autoCorrectionThreshold = Float.parseFloat(val); 279 } 280 } 281 } catch (NumberFormatException e) { 282 // Whenever the threshold settings are correct, never come here. 283 autoCorrectionThreshold = Float.MAX_VALUE; 284 Log.w(TAG, "Cannot load auto correction threshold setting." 285 + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting 286 + ", autoCorrectionThresholdValues: " 287 + Arrays.toString(autoCorrectionThresholdValues), e); 288 } 289 return autoCorrectionThreshold; 290 } 291 } 292