Settings.java revision a37bee2805dc1533092a25f1ee6633b78bff0070
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
17package com.android.inputmethod.latin.settings;
18
19import android.content.Context;
20import android.content.SharedPreferences;
21import android.content.pm.ApplicationInfo;
22import android.content.res.Resources;
23import android.preference.PreferenceManager;
24import android.util.Log;
25
26import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
27import com.android.inputmethod.latin.InputAttributes;
28import com.android.inputmethod.latin.R;
29import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
30import com.android.inputmethod.latin.utils.LocaleUtils;
31import com.android.inputmethod.latin.utils.ResourceUtils;
32import com.android.inputmethod.latin.utils.RunInLocale;
33import com.android.inputmethod.latin.utils.StringUtils;
34
35import java.util.HashMap;
36import java.util.Locale;
37import java.util.concurrent.locks.ReentrantLock;
38
39public 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    private Resources mRes;
102    private SharedPreferences mPrefs;
103    private SettingsValues mSettingsValues;
104    private final ReentrantLock mSettingsValuesLock = new ReentrantLock();
105
106    private static final Settings sInstance = new Settings();
107
108    public static Settings getInstance() {
109        return sInstance;
110    }
111
112    public static void init(final Context context) {
113        sInstance.onCreate(context);
114    }
115
116    private Settings() {
117        // Intentional empty constructor for singleton.
118    }
119
120    private void onCreate(final Context context) {
121        mRes = context.getResources();
122        mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
123        mPrefs.registerOnSharedPreferenceChangeListener(this);
124    }
125
126    public void onDestroy() {
127        mPrefs.unregisterOnSharedPreferenceChangeListener(this);
128    }
129
130    @Override
131    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
132        mSettingsValuesLock.lock();
133        try {
134            if (mSettingsValues == null) {
135                // TODO: Introduce a static function to register this class and ensure that
136                // loadSettings must be called before "onSharedPreferenceChanged" is called.
137                Log.w(TAG, "onSharedPreferenceChanged called before loadSettings.");
138                return;
139            }
140            loadSettings(mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
141        } finally {
142            mSettingsValuesLock.unlock();
143        }
144    }
145
146    public void loadSettings(final Locale locale, final InputAttributes inputAttributes) {
147        mSettingsValuesLock.lock();
148        try {
149            final SharedPreferences prefs = mPrefs;
150            final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
151                @Override
152                protected SettingsValues job(final Resources res) {
153                    return new SettingsValues(prefs, locale, res, inputAttributes);
154                }
155            };
156            mSettingsValues = job.runInLocale(mRes, locale);
157        } finally {
158            mSettingsValuesLock.unlock();
159        }
160    }
161
162    // TODO: Remove this method and add proxy method to SettingsValues.
163    public SettingsValues getCurrent() {
164        return mSettingsValues;
165    }
166
167    public boolean isInternal() {
168        return mSettingsValues.mIsInternal;
169    }
170
171    public String getWordSeparators() {
172        return mSettingsValues.mWordSeparators;
173    }
174
175    public boolean isWordSeparator(final int code) {
176        return mSettingsValues.isWordSeparator(code);
177    }
178
179    public boolean getBlockPotentiallyOffensive() {
180        return mSettingsValues.mBlockPotentiallyOffensive;
181    }
182
183    // Accessed from the settings interface, hence public
184    public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
185            final Resources res) {
186        return prefs.getBoolean(Settings.PREF_SOUND_ON,
187                res.getBoolean(R.bool.config_default_sound_enabled));
188    }
189
190    public static boolean readVibrationEnabled(final SharedPreferences prefs,
191            final Resources res) {
192        final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
193        return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON,
194                res.getBoolean(R.bool.config_default_vibration_enabled));
195    }
196
197    public static boolean readAutoCorrectEnabled(final String currentAutoCorrectionSetting,
198            final Resources res) {
199        final String autoCorrectionOff = res.getString(
200                R.string.auto_correction_threshold_mode_index_off);
201        return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
202    }
203
204    public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
205            final Resources res) {
206        return prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE,
207                res.getBoolean(R.bool.config_block_potentially_offensive));
208    }
209
210    public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
211        return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
212    }
213
214    public static boolean readGestureInputEnabled(final SharedPreferences prefs,
215            final Resources res) {
216        return readFromBuildConfigIfGestureInputEnabled(res)
217                && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
218    }
219
220    public static boolean readPhraseGestureEnabled(final SharedPreferences prefs,
221            final Resources res) {
222        return prefs.getBoolean(Settings.PREF_PHRASE_GESTURE_ENABLED,
223                res.getBoolean(R.bool.config_default_phrase_gesture_enabled));
224    }
225
226    public static boolean readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(
227            final Resources res) {
228        return res.getBoolean(R.bool.config_enable_show_option_of_key_preview_popup);
229    }
230
231    public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs,
232            final Resources res) {
233        final boolean defaultKeyPreviewPopup = res.getBoolean(
234                R.bool.config_default_key_preview_popup);
235        if (!readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) {
236            return defaultKeyPreviewPopup;
237        }
238        return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup);
239    }
240
241    public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs,
242            final Resources res) {
243        return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
244                Integer.toString(res.getInteger(
245                        R.integer.config_key_preview_linger_timeout))));
246    }
247
248    public static boolean readShowsLanguageSwitchKey(final SharedPreferences prefs) {
249        if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
250            final boolean suppressLanguageSwitchKey = prefs.getBoolean(
251                    PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
252            final SharedPreferences.Editor editor = prefs.edit();
253            editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY);
254            editor.putBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey);
255            editor.apply();
256        }
257        return prefs.getBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
258    }
259
260    public static String readPrefAdditionalSubtypes(final SharedPreferences prefs,
261            final Resources res) {
262        final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(
263                res.getStringArray(R.array.predefined_subtypes));
264        return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes);
265    }
266
267    public static void writePrefAdditionalSubtypes(final SharedPreferences prefs,
268            final String prefSubtypes) {
269        prefs.edit().putString(Settings.PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply();
270    }
271
272    public static float readKeypressSoundVolume(final SharedPreferences prefs,
273            final Resources res) {
274        final float volume = prefs.getFloat(PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
275        return (volume >= 0) ? volume : readDefaultKeypressSoundVolume(res);
276    }
277
278    public static float readDefaultKeypressSoundVolume(final Resources res) {
279        return Float.parseFloat(
280                ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes));
281    }
282
283    public static int readKeyLongpressTimeout(final SharedPreferences prefs,
284            final Resources res) {
285        final int ms = prefs.getInt(PREF_KEY_LONGPRESS_TIMEOUT, -1);
286        return (ms >= 0) ? ms : readDefaultKeyLongpressTimeout(res);
287    }
288
289    public static int readDefaultKeyLongpressTimeout(final Resources res) {
290        return res.getInteger(R.integer.config_default_longpress_key_timeout);
291    }
292
293    public static int readKeypressVibrationDuration(final SharedPreferences prefs,
294            final Resources res) {
295        final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1);
296        return (ms >= 0) ? ms : readDefaultKeypressVibrationDuration(res);
297    }
298
299    public static int readDefaultKeypressVibrationDuration(final Resources res) {
300        return Integer.parseInt(
301                ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations));
302    }
303
304    public static boolean readUsabilityStudyMode(final SharedPreferences prefs) {
305        return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
306    }
307
308    public static long readLastUserHistoryWriteTime(final SharedPreferences prefs,
309            final String locale) {
310        final String str = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
311        final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str);
312        if (map.containsKey(locale)) {
313            return map.get(locale);
314        }
315        return 0;
316    }
317
318    public static void writeLastUserHistoryWriteTime(final SharedPreferences prefs,
319            final String locale) {
320        final String oldStr = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
321        final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr);
322        map.put(locale, System.currentTimeMillis());
323        final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map);
324        prefs.edit().putString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
325    }
326
327    public static boolean readUseFullscreenMode(final Resources res) {
328        return res.getBoolean(R.bool.config_use_fullscreen_mode);
329    }
330
331    public static boolean readShowSetupWizardIcon(final SharedPreferences prefs,
332            final Context context) {
333        final boolean enableSetupWizardByConfig = context.getResources().getBoolean(
334                R.bool.config_setup_wizard_available);
335        if (!enableSetupWizardByConfig) {
336            return false;
337        }
338        if (!prefs.contains(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) {
339            final ApplicationInfo appInfo = context.getApplicationInfo();
340            final boolean isApplicationInSystemImage =
341                    (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
342            // Default value
343            return !isApplicationInSystemImage;
344        }
345        return prefs.getBoolean(Settings.PREF_SHOW_SETUP_WIZARD_ICON, false);
346    }
347
348    public static boolean isInternal(final SharedPreferences prefs) {
349        return prefs.getBoolean(Settings.PREF_KEY_IS_INTERNAL, false);
350    }
351
352    public static boolean readUseOnlyPersonalizationDictionaryForDebug(
353            final SharedPreferences prefs) {
354        return prefs.getBoolean(
355                DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false);
356    }
357
358    public static boolean readBoostPersonalizationDictionaryForDebug(
359            final SharedPreferences prefs) {
360        return prefs.getBoolean(
361                DebugSettings.PREF_BOOST_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false);
362    }
363
364    public void writeLastUsedPersonalizationToken(byte[] token) {
365        final String tokenStr = StringUtils.byteArrayToHexString(token);
366        mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply();
367    }
368
369    public byte[] readLastUsedPersonalizationToken() {
370        final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null);
371        return StringUtils.hexStringToByteArray(tokenStr);
372    }
373}
374