Settings.java revision 0b593ce858d4b406e949f0e809cb77f41da691f9
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.ResourceUtils;
31import com.android.inputmethod.latin.utils.RunInLocale;
32import com.android.inputmethod.latin.utils.StringUtils;
33
34import java.util.Locale;
35import java.util.concurrent.locks.ReentrantLock;
36
37public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener {
38    private static final String TAG = Settings.class.getSimpleName();
39    // In the same order as xml/prefs.xml
40    public static final String PREF_GENERAL_SETTINGS = "general_settings";
41    public static final String PREF_AUTO_CAP = "auto_cap";
42    public static final String PREF_VIBRATE_ON = "vibrate_on";
43    public static final String PREF_SOUND_ON = "sound_on";
44    public static final String PREF_POPUP_ON = "popup_on";
45    // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead.
46    public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode";
47    public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
48    public static final String PREF_CORRECTION_SETTINGS = "correction_settings";
49    public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
50    public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
51    public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
52    public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
53    public static final String PREF_MISC_SETTINGS = "misc_settings";
54    public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
55    public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
56    public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts";
57    public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
58            "pref_key_use_double_space_period";
59    public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
60            "pref_key_block_potentially_offensive";
61    public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
62            "pref_show_language_switch_key";
63    public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
64            "pref_include_other_imes_in_language_switch_list";
65    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
66    public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
67    public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
68            "pref_key_preview_popup_dismiss_delay";
69    public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
70    public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings";
71    public static final String PREF_GESTURE_INPUT = "gesture_input";
72    public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview";
73    public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
74    public static final String PREF_VIBRATION_DURATION_SETTINGS =
75            "pref_vibration_duration_settings";
76    public static final String PREF_KEYPRESS_SOUND_VOLUME =
77            "pref_keypress_sound_volume";
78    public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
79    public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
80            "pref_gesture_floating_preview_text";
81    public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
82    public static final String PREF_PHRASE_GESTURE_ENABLED = "pref_gesture_space_aware";
83
84    public static final String PREF_INPUT_LANGUAGE = "input_language";
85    public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
86    public static final String PREF_DEBUG_SETTINGS = "debug_settings";
87    public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
88
89    // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
90    // This is being used only for the backward compatibility.
91    private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
92            "pref_suppress_language_switch_key";
93
94    private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN =
95            "pref_last_used_personalization_token";
96    public static final String PREF_SEND_FEEDBACK = "send_feedback";
97    public static final String PREF_ABOUT_KEYBOARD = "about_keyboard";
98
99    // Emoji
100    public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys";
101    public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
102    public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
103
104    private Context mContext;
105    private Resources mRes;
106    private SharedPreferences mPrefs;
107    private SettingsValues mSettingsValues;
108    private final ReentrantLock mSettingsValuesLock = new ReentrantLock();
109
110    private static final Settings sInstance = new Settings();
111
112    public static Settings getInstance() {
113        return sInstance;
114    }
115
116    public static void init(final Context context) {
117        sInstance.onCreate(context);
118    }
119
120    private Settings() {
121        // Intentional empty constructor for singleton.
122    }
123
124    private void onCreate(final Context context) {
125        mContext = context;
126        mRes = context.getResources();
127        mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
128        mPrefs.registerOnSharedPreferenceChangeListener(this);
129    }
130
131    public void onDestroy() {
132        mPrefs.unregisterOnSharedPreferenceChangeListener(this);
133    }
134
135    @Override
136    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
137        mSettingsValuesLock.lock();
138        try {
139            if (mSettingsValues == null) {
140                // TODO: Introduce a static function to register this class and ensure that
141                // loadSettings must be called before "onSharedPreferenceChanged" is called.
142                Log.w(TAG, "onSharedPreferenceChanged called before loadSettings.");
143                return;
144            }
145            loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
146        } finally {
147            mSettingsValuesLock.unlock();
148        }
149    }
150
151    public void loadSettings(final Context context, final Locale locale,
152            final InputAttributes inputAttributes) {
153        mSettingsValuesLock.lock();
154        mContext = context;
155        try {
156            final SharedPreferences prefs = mPrefs;
157            final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
158                @Override
159                protected SettingsValues job(final Resources res) {
160                    return new SettingsValues(context, prefs, locale, res, inputAttributes);
161                }
162            };
163            mSettingsValues = job.runInLocale(mRes, locale);
164        } finally {
165            mSettingsValuesLock.unlock();
166        }
167    }
168
169    // TODO: Remove this method and add proxy method to SettingsValues.
170    public SettingsValues getCurrent() {
171        return mSettingsValues;
172    }
173
174    public boolean isInternal() {
175        return mSettingsValues.mIsInternal;
176    }
177
178    public String getWordSeparators() {
179        return mSettingsValues.mSpacingAndPunctuations.mWordSeparators;
180    }
181
182    public boolean isWordSeparator(final int code) {
183        return mSettingsValues.isWordSeparator(code);
184    }
185
186    public boolean getBlockPotentiallyOffensive() {
187        return mSettingsValues.mBlockPotentiallyOffensive;
188    }
189
190    // Accessed from the settings interface, hence public
191    public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
192            final Resources res) {
193        return prefs.getBoolean(Settings.PREF_SOUND_ON,
194                res.getBoolean(R.bool.config_default_sound_enabled));
195    }
196
197    public static boolean readVibrationEnabled(final SharedPreferences prefs,
198            final Resources res) {
199        final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
200        return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON,
201                res.getBoolean(R.bool.config_default_vibration_enabled));
202    }
203
204    public static boolean readAutoCorrectEnabled(final String currentAutoCorrectionSetting,
205            final Resources res) {
206        final String autoCorrectionOff = res.getString(
207                R.string.auto_correction_threshold_mode_index_off);
208        return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
209    }
210
211    public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
212            final Resources res) {
213        return prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE,
214                res.getBoolean(R.bool.config_block_potentially_offensive));
215    }
216
217    public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
218        return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
219    }
220
221    public static boolean readGestureInputEnabled(final SharedPreferences prefs,
222            final Resources res) {
223        return readFromBuildConfigIfGestureInputEnabled(res)
224                && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
225    }
226
227    public static boolean readPhraseGestureEnabled(final SharedPreferences prefs,
228            final Resources res) {
229        return prefs.getBoolean(Settings.PREF_PHRASE_GESTURE_ENABLED,
230                res.getBoolean(R.bool.config_default_phrase_gesture_enabled));
231    }
232
233    public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) {
234        return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option);
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 (!readFromBuildConfigIfToShowKeyPreviewPopupOption(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 boolean readUseFullscreenMode(final Resources res) {
337        return res.getBoolean(R.bool.config_use_fullscreen_mode);
338    }
339
340    public static boolean readShowSetupWizardIcon(final SharedPreferences prefs,
341            final Context context) {
342        final boolean enableSetupWizardByConfig = context.getResources().getBoolean(
343                R.bool.config_setup_wizard_available);
344        if (!enableSetupWizardByConfig) {
345            return false;
346        }
347        if (!prefs.contains(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) {
348            final ApplicationInfo appInfo = context.getApplicationInfo();
349            final boolean isApplicationInSystemImage =
350                    (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
351            // Default value
352            return !isApplicationInSystemImage;
353        }
354        return prefs.getBoolean(Settings.PREF_SHOW_SETUP_WIZARD_ICON, false);
355    }
356
357    public static boolean isInternal(final SharedPreferences prefs) {
358        return prefs.getBoolean(Settings.PREF_KEY_IS_INTERNAL, false);
359    }
360
361    public void writeLastUsedPersonalizationToken(byte[] token) {
362        if (token == null) {
363            mPrefs.edit().remove(PREF_LAST_USED_PERSONALIZATION_TOKEN).apply();
364        } else {
365            final String tokenStr = StringUtils.byteArrayToHexString(token);
366            mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply();
367        }
368    }
369
370    public byte[] readLastUsedPersonalizationToken() {
371        final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null);
372        return StringUtils.hexStringToByteArray(tokenStr);
373    }
374
375    public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) {
376        prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply();
377    }
378
379    public static String readEmojiRecentKeys(final SharedPreferences prefs) {
380        return prefs.getString(PREF_EMOJI_RECENT_KEYS, "");
381    }
382
383    public static void writeLastTypedEmojiCategoryPageId(
384            final SharedPreferences prefs, final int categoryId, final int categoryPageId) {
385        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
386        prefs.edit().putInt(key, categoryPageId).apply();
387    }
388
389    public static int readLastTypedEmojiCategoryPageId(
390            final SharedPreferences prefs, final int categoryId) {
391        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
392        return prefs.getInt(key, 0);
393    }
394
395    public static void writeLastShownEmojiCategoryId(
396            final SharedPreferences prefs, final int categoryId) {
397        prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply();
398    }
399
400    public static int readLastShownEmojiCategoryId(
401            final SharedPreferences prefs, final int defValue) {
402        return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue);
403    }
404}
405