Settings.java revision a715d7f6fd3b29e660d78f83815ebe75837f1436
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.os.Build;
24import android.preference.PreferenceManager;
25import android.util.Log;
26
27import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
28import com.android.inputmethod.latin.InputAttributes;
29import com.android.inputmethod.latin.R;
30import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
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.Collections;
36import java.util.Locale;
37import java.util.Set;
38import java.util.concurrent.locks.ReentrantLock;
39
40public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener {
41    private static final String TAG = Settings.class.getSimpleName();
42    // Settings screens
43    public static final String SCREEN_INPUT = "screen_input";
44    public static final String SCREEN_THEME = "screen_theme";
45    public static final String SCREEN_MULTI_LINGUAL = "screen_multi_lingual";
46    public static final String SCREEN_GESTURE = "screen_gesture";
47    public static final String SCREEN_CORRECTION = "screen_correction";
48    public static final String SCREEN_ADVANCED = "screen_advanced";
49    public static final String SCREEN_DEBUG = "screen_debug";
50    // In the same order as xml/prefs.xml
51    public static final String PREF_AUTO_CAP = "auto_cap";
52    public static final String PREF_VIBRATE_ON = "vibrate_on";
53    public static final String PREF_SOUND_ON = "sound_on";
54    public static final String PREF_POPUP_ON = "popup_on";
55    // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead.
56    public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode";
57    public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
58    public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
59    public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
60    public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
61    // PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE is obsolete. Use PREF_SHOW_SUGGESTIONS instead.
62    public static final String PREF_SHOW_SUGGESTIONS_SETTING_OBSOLETE = "show_suggestions_setting";
63    public static final String PREF_SHOW_SUGGESTIONS = "show_suggestions";
64    public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
65    public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts";
66    public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
67            "pref_key_use_double_space_period";
68    public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
69            "pref_key_block_potentially_offensive";
70    public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS =
71            (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
72            || (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
73                    && Build.VERSION.CODENAME.equals("REL"));
74    public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
75            "pref_show_language_switch_key";
76    public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
77            "pref_include_other_imes_in_language_switch_list";
78    public static final String PREF_KEYBOARD_THEME = "pref_keyboard_theme";
79    public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
80    // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
81    public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
82            "pref_key_preview_popup_dismiss_delay";
83    public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
84    public static final String PREF_GESTURE_INPUT = "gesture_input";
85    public static final String PREF_VIBRATION_DURATION_SETTINGS =
86            "pref_vibration_duration_settings";
87    public static final String PREF_KEYPRESS_SOUND_VOLUME =
88            "pref_keypress_sound_volume";
89    public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
90    public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
91            "pref_gesture_floating_preview_text";
92    public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
93    public static final String PREF_PHRASE_GESTURE_ENABLED = "pref_gesture_space_aware";
94
95    public static final String PREF_INPUT_LANGUAGE = "input_language";
96    public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
97    public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
98
99    public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
100
101    // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
102    // This is being used only for the backward compatibility.
103    private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
104            "pref_suppress_language_switch_key";
105
106    private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN =
107            "pref_last_used_personalization_token";
108    private static final String PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME =
109            "pref_last_used_personalization_dict_wiped_time";
110    private static final String PREF_CORPUS_HANDLES_FOR_PERSONALIZATION =
111            "pref_corpus_handles_for_personalization";
112
113    // Emoji
114    public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys";
115    public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
116    public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id";
117
118    private static final float UNDEFINED_PREFERENCE_VALUE_FLOAT = -1.0f;
119    private static final int UNDEFINED_PREFERENCE_VALUE_INT = -1;
120
121    private Context mContext;
122    private Resources mRes;
123    private SharedPreferences mPrefs;
124    private SettingsValues mSettingsValues;
125    private final ReentrantLock mSettingsValuesLock = new ReentrantLock();
126
127    private static final Settings sInstance = new Settings();
128
129    public static Settings getInstance() {
130        return sInstance;
131    }
132
133    public static void init(final Context context) {
134        sInstance.onCreate(context);
135    }
136
137    private Settings() {
138        // Intentional empty constructor for singleton.
139    }
140
141    private void onCreate(final Context context) {
142        mContext = context;
143        mRes = context.getResources();
144        mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
145        mPrefs.registerOnSharedPreferenceChangeListener(this);
146    }
147
148    public void onDestroy() {
149        mPrefs.unregisterOnSharedPreferenceChangeListener(this);
150    }
151
152    @Override
153    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
154        mSettingsValuesLock.lock();
155        try {
156            if (mSettingsValues == null) {
157                // TODO: Introduce a static function to register this class and ensure that
158                // loadSettings must be called before "onSharedPreferenceChanged" is called.
159                Log.w(TAG, "onSharedPreferenceChanged called before loadSettings.");
160                return;
161            }
162            loadSettings(mContext, mSettingsValues.mLocale, mSettingsValues.mInputAttributes);
163        } finally {
164            mSettingsValuesLock.unlock();
165        }
166    }
167
168    public void loadSettings(final Context context, final Locale locale,
169            final InputAttributes inputAttributes) {
170        mSettingsValuesLock.lock();
171        mContext = context;
172        try {
173            final SharedPreferences prefs = mPrefs;
174            final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
175                @Override
176                protected SettingsValues job(final Resources res) {
177                    return new SettingsValues(context, prefs, res, inputAttributes);
178                }
179            };
180            mSettingsValues = job.runInLocale(mRes, locale);
181        } finally {
182            mSettingsValuesLock.unlock();
183        }
184    }
185
186    // TODO: Remove this method and add proxy method to SettingsValues.
187    public SettingsValues getCurrent() {
188        return mSettingsValues;
189    }
190
191    public boolean isInternal() {
192        return mSettingsValues.mIsInternal;
193    }
194
195    public boolean isWordSeparator(final int code) {
196        return mSettingsValues.isWordSeparator(code);
197    }
198
199    public boolean getBlockPotentiallyOffensive() {
200        return mSettingsValues.mBlockPotentiallyOffensive;
201    }
202
203    // Accessed from the settings interface, hence public
204    public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
205            final Resources res) {
206        return prefs.getBoolean(PREF_SOUND_ON,
207                res.getBoolean(R.bool.config_default_sound_enabled));
208    }
209
210    public static boolean readVibrationEnabled(final SharedPreferences prefs,
211            final Resources res) {
212        final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
213        return hasVibrator && prefs.getBoolean(PREF_VIBRATE_ON,
214                res.getBoolean(R.bool.config_default_vibration_enabled));
215    }
216
217    public static boolean readAutoCorrectEnabled(final String currentAutoCorrectionSetting,
218            final Resources res) {
219        final String autoCorrectionOff = res.getString(
220                R.string.auto_correction_threshold_mode_index_off);
221        return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
222    }
223
224    public static boolean readBlockPotentiallyOffensive(final SharedPreferences prefs,
225            final Resources res) {
226        return prefs.getBoolean(PREF_BLOCK_POTENTIALLY_OFFENSIVE,
227                res.getBoolean(R.bool.config_block_potentially_offensive));
228    }
229
230    public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
231        return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
232    }
233
234    public static boolean readGestureInputEnabled(final SharedPreferences prefs,
235            final Resources res) {
236        return readFromBuildConfigIfGestureInputEnabled(res)
237                && prefs.getBoolean(PREF_GESTURE_INPUT, true);
238    }
239
240    public static boolean readPhraseGestureEnabled(final SharedPreferences prefs,
241            final Resources res) {
242        return prefs.getBoolean(PREF_PHRASE_GESTURE_ENABLED,
243                res.getBoolean(R.bool.config_default_phrase_gesture_enabled));
244    }
245
246    public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) {
247        return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option);
248    }
249
250    public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs,
251            final Resources res) {
252        final boolean defaultKeyPreviewPopup = res.getBoolean(
253                R.bool.config_default_key_preview_popup);
254        if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
255            return defaultKeyPreviewPopup;
256        }
257        return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup);
258    }
259
260    public static int readKeyPreviewPopupDismissDelay(final SharedPreferences prefs,
261            final Resources res) {
262        return Integer.parseInt(prefs.getString(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
263                Integer.toString(res.getInteger(
264                        R.integer.config_key_preview_linger_timeout))));
265    }
266
267    public static boolean readShowsLanguageSwitchKey(final SharedPreferences prefs) {
268        if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
269            final boolean suppressLanguageSwitchKey = prefs.getBoolean(
270                    PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
271            final SharedPreferences.Editor editor = prefs.edit();
272            editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY);
273            editor.putBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey);
274            editor.apply();
275        }
276        return prefs.getBoolean(PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
277    }
278
279    public static String readPrefAdditionalSubtypes(final SharedPreferences prefs,
280            final Resources res) {
281        final String predefinedPrefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(
282                res.getStringArray(R.array.predefined_subtypes));
283        return prefs.getString(PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes);
284    }
285
286    public static void writePrefAdditionalSubtypes(final SharedPreferences prefs,
287            final String prefSubtypes) {
288        prefs.edit().putString(PREF_CUSTOM_INPUT_STYLES, prefSubtypes).apply();
289    }
290
291    public static float readKeypressSoundVolume(final SharedPreferences prefs,
292            final Resources res) {
293        final float volume = prefs.getFloat(
294                PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT);
295        return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume
296                : readDefaultKeypressSoundVolume(res);
297    }
298
299    // Default keypress sound volume for unknown devices.
300    // The negative value means system default.
301    private static final String DEFAULT_KEYPRESS_SOUND_VOLUME = Float.toString(-1.0f);
302
303    public static float readDefaultKeypressSoundVolume(final Resources res) {
304        return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(res,
305                R.array.keypress_volumes, DEFAULT_KEYPRESS_SOUND_VOLUME));
306    }
307
308    public static int readKeyLongpressTimeout(final SharedPreferences prefs,
309            final Resources res) {
310        final int milliseconds = prefs.getInt(
311                DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT);
312        return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
313                : readDefaultKeyLongpressTimeout(res);
314    }
315
316    public static int readDefaultKeyLongpressTimeout(final Resources res) {
317        return res.getInteger(R.integer.config_default_longpress_key_timeout);
318    }
319
320    public static int readKeypressVibrationDuration(final SharedPreferences prefs,
321            final Resources res) {
322        final int milliseconds = prefs.getInt(
323                PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT);
324        return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds
325                : readDefaultKeypressVibrationDuration(res);
326    }
327
328    // Default keypress vibration duration for unknown devices.
329    // The negative value means system default.
330    private static final String DEFAULT_KEYPRESS_VIBRATION_DURATION = Integer.toString(-1);
331
332    public static int readDefaultKeypressVibrationDuration(final Resources res) {
333        return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(res,
334                R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION));
335    }
336
337    public static float readKeyPreviewAnimationScale(final SharedPreferences prefs,
338            final String prefKey, final float defaultValue) {
339        final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT);
340        return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue;
341    }
342
343    public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs,
344            final String prefKey, final int defaultValue) {
345        final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT);
346        return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue;
347    }
348
349    public static boolean readUseFullscreenMode(final Resources res) {
350        return res.getBoolean(R.bool.config_use_fullscreen_mode);
351    }
352
353    public static boolean readShowSetupWizardIcon(final SharedPreferences prefs,
354            final Context context) {
355        final boolean enableSetupWizardByConfig = context.getResources().getBoolean(
356                R.bool.config_setup_wizard_available);
357        if (!enableSetupWizardByConfig) {
358            return false;
359        }
360        if (!prefs.contains(PREF_SHOW_SETUP_WIZARD_ICON)) {
361            final ApplicationInfo appInfo = context.getApplicationInfo();
362            final boolean isApplicationInSystemImage =
363                    (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
364            // Default value
365            return !isApplicationInSystemImage;
366        }
367        return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false);
368    }
369
370    public static boolean isInternal(final SharedPreferences prefs) {
371        return prefs.getBoolean(PREF_KEY_IS_INTERNAL, false);
372    }
373
374    public void writeLastUsedPersonalizationToken(byte[] token) {
375        if (token == null) {
376            mPrefs.edit().remove(PREF_LAST_USED_PERSONALIZATION_TOKEN).apply();
377        } else {
378            final String tokenStr = StringUtils.byteArrayToHexString(token);
379            mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply();
380        }
381    }
382
383    public byte[] readLastUsedPersonalizationToken() {
384        final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null);
385        return StringUtils.hexStringToByteArray(tokenStr);
386    }
387
388    public void writeLastPersonalizationDictWipedTime(final long timestamp) {
389        mPrefs.edit().putLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, timestamp).apply();
390    }
391
392    public long readLastPersonalizationDictGeneratedTime() {
393        return mPrefs.getLong(PREF_LAST_PERSONALIZATION_DICT_WIPED_TIME, 0);
394    }
395
396    public void writeCorpusHandlesForPersonalization(final Set<String> corpusHandles) {
397        mPrefs.edit().putStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, corpusHandles).apply();
398    }
399
400    public Set<String> readCorpusHandlesForPersonalization() {
401        final Set<String> emptySet = Collections.emptySet();
402        return mPrefs.getStringSet(PREF_CORPUS_HANDLES_FOR_PERSONALIZATION, emptySet);
403    }
404
405    public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) {
406        prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply();
407    }
408
409    public static String readEmojiRecentKeys(final SharedPreferences prefs) {
410        return prefs.getString(PREF_EMOJI_RECENT_KEYS, "");
411    }
412
413    public static void writeLastTypedEmojiCategoryPageId(
414            final SharedPreferences prefs, final int categoryId, final int categoryPageId) {
415        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
416        prefs.edit().putInt(key, categoryPageId).apply();
417    }
418
419    public static int readLastTypedEmojiCategoryPageId(
420            final SharedPreferences prefs, final int categoryId) {
421        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + categoryId;
422        return prefs.getInt(key, 0);
423    }
424
425    public static void writeLastShownEmojiCategoryId(
426            final SharedPreferences prefs, final int categoryId) {
427        prefs.edit().putInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, categoryId).apply();
428    }
429
430    public static int readLastShownEmojiCategoryId(
431            final SharedPreferences prefs, final int defValue) {
432        return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_ID, defValue);
433    }
434}
435