SettingsFragment.java revision 59c3ef1ff8df23e3c3e3f549c0289c479553c666
1/*
2 * Copyright (C) 2008 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.app.Activity;
20import android.app.backup.BackupManager;
21import android.content.Context;
22import android.content.Intent;
23import android.content.SharedPreferences;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.res.Resources;
27import android.media.AudioManager;
28import android.os.Build;
29import android.os.Bundle;
30import android.preference.CheckBoxPreference;
31import android.preference.ListPreference;
32import android.preference.Preference;
33import android.preference.Preference.OnPreferenceClickListener;
34import android.preference.PreferenceGroup;
35import android.preference.PreferenceScreen;
36import android.util.Log;
37import android.view.inputmethod.InputMethodSubtype;
38
39import com.android.inputmethod.dictionarypack.DictionarySettingsActivity;
40import com.android.inputmethod.keyboard.KeyboardTheme;
41import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
42import com.android.inputmethod.latin.R;
43import com.android.inputmethod.latin.SubtypeSwitcher;
44import com.android.inputmethod.latin.define.ProductionFlag;
45import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager;
46import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
47import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
48import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
49import com.android.inputmethod.latin.utils.ApplicationUtils;
50import com.android.inputmethod.latin.utils.FeedbackUtils;
51import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
52import com.android.inputmethodcommon.InputMethodSettingsFragment;
53
54import java.util.TreeSet;
55
56public final class SettingsFragment extends InputMethodSettingsFragment
57        implements SharedPreferences.OnSharedPreferenceChangeListener {
58    private static final String TAG = SettingsFragment.class.getSimpleName();
59    private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false;
60    private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS =
61            DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
62                    || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */;
63
64    private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) {
65        final Preference preference = findPreference(preferenceKey);
66        if (preference != null) {
67            preference.setEnabled(enabled);
68        }
69    }
70
71    private void updateListPreferenceSummaryToCurrentValue(final String prefKey) {
72        // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before
73        // KitKat, we need to update the summary programmatically.
74        final ListPreference listPreference = (ListPreference)findPreference(prefKey);
75        if (listPreference == null) {
76            return;
77        }
78        final CharSequence entries[] = listPreference.getEntries();
79        final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue());
80        listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]);
81    }
82
83    private static void removePreference(final String preferenceKey, final PreferenceGroup parent) {
84        if (parent == null) {
85            return;
86        }
87        final Preference preference = parent.findPreference(preferenceKey);
88        if (preference != null) {
89            parent.removePreference(preference);
90        }
91    }
92
93    @Override
94    public void onCreate(final Bundle icicle) {
95        super.onCreate(icicle);
96        setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
97        setSubtypeEnablerTitle(R.string.select_language);
98        addPreferencesFromResource(R.xml.prefs);
99        final PreferenceScreen preferenceScreen = getPreferenceScreen();
100        if (preferenceScreen != null) {
101            preferenceScreen.setTitle(
102                    ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class));
103        }
104
105        final Resources res = getResources();
106        final Context context = getActivity();
107
108        // When we are called from the Settings application but we are not already running, some
109        // singleton and utility classes may not have been initialized.  We have to call
110        // initialization method of these classes here. See {@link LatinIME#onCreate()}.
111        SubtypeSwitcher.init(context);
112        SubtypeLocaleUtils.init(context);
113        AudioAndHapticFeedbackManager.init(context);
114
115        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
116        prefs.registerOnSharedPreferenceChangeListener(this);
117
118        ensureConsistencyOfAutoCorrectionSettings();
119
120        final PreferenceGroup generalSettings =
121                (PreferenceGroup) findPreference(Settings.PREF_GENERAL_SETTINGS);
122        final PreferenceGroup miscSettings =
123                (PreferenceGroup) findPreference(Settings.PREF_MISC_SETTINGS);
124
125        final Preference debugSettings = findPreference(Settings.PREF_DEBUG_SETTINGS);
126        if (debugSettings != null) {
127            if (Settings.isInternal(prefs)) {
128                final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
129                debugSettingsIntent.setClassName(
130                        context.getPackageName(), DebugSettingsActivity.class.getName());
131                debugSettings.setIntent(debugSettingsIntent);
132            } else {
133                miscSettings.removePreference(debugSettings);
134            }
135        }
136
137        final Preference feedbackSettings = findPreference(Settings.PREF_SEND_FEEDBACK);
138        final Preference aboutSettings = findPreference(Settings.PREF_ABOUT_KEYBOARD);
139        if (feedbackSettings != null) {
140            if (FeedbackUtils.isFeedbackFormSupported()) {
141                feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
142                    @Override
143                    public boolean onPreferenceClick(final Preference pref) {
144                        FeedbackUtils.showFeedbackForm(getActivity());
145                        return true;
146                    }
147                });
148                aboutSettings.setTitle(FeedbackUtils.getAboutKeyboardTitleResId());
149                aboutSettings.setIntent(FeedbackUtils.getAboutKeyboardIntent(getActivity()));
150            } else {
151                miscSettings.removePreference(feedbackSettings);
152                miscSettings.removePreference(aboutSettings);
153            }
154        }
155        if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
156            // The about screen contains items that may be confusing in development-only versions.
157            miscSettings.removePreference(aboutSettings);
158        }
159
160        final boolean showVoiceKeyOption = res.getBoolean(
161                R.bool.config_enable_show_voice_key_option);
162        if (!showVoiceKeyOption) {
163            removePreference(Settings.PREF_VOICE_INPUT_KEY, generalSettings);
164        }
165
166        final PreferenceGroup advancedSettings =
167                (PreferenceGroup) findPreference(Settings.PREF_ADVANCED_SETTINGS);
168        if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) {
169            removePreference(Settings.PREF_VIBRATE_ON, generalSettings);
170            removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings);
171        }
172
173        // TODO: consolidate key preview dismiss delay with the key preview animation parameters.
174        if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
175            removePreference(Settings.PREF_POPUP_ON, generalSettings);
176            removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings);
177        } else {
178            // TODO: Cleanup this setup.
179            final ListPreference keyPreviewPopupDismissDelay =
180                    (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
181            final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
182                    R.integer.config_key_preview_linger_timeout));
183            keyPreviewPopupDismissDelay.setEntries(new String[] {
184                    res.getString(R.string.key_preview_popup_dismiss_no_delay),
185                    res.getString(R.string.key_preview_popup_dismiss_default_delay),
186            });
187            keyPreviewPopupDismissDelay.setEntryValues(new String[] {
188                    "0",
189                    popupDismissDelayDefaultValue
190            });
191            if (null == keyPreviewPopupDismissDelay.getValue()) {
192                keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
193            }
194            keyPreviewPopupDismissDelay.setEnabled(
195                    Settings.readKeyPreviewPopupEnabled(prefs, res));
196        }
197
198        if (!res.getBoolean(R.bool.config_setup_wizard_available)) {
199            removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedSettings);
200        }
201
202        setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
203                Settings.readShowsLanguageSwitchKey(prefs));
204
205        final PreferenceGroup textCorrectionGroup =
206                (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS);
207        final PreferenceScreen dictionaryLink =
208                (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY);
209        final Intent intent = dictionaryLink.getIntent();
210        intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName());
211        final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
212        if (0 >= number) {
213            textCorrectionGroup.removePreference(dictionaryLink);
214        }
215
216        final Preference editPersonalDictionary =
217                findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY);
218        final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent();
219        final ResolveInfo ri = USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS ? null
220                : context.getPackageManager().resolveActivity(
221                        editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY);
222        if (ri == null) {
223            overwriteUserDictionaryPreference(editPersonalDictionary);
224        }
225
226        if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
227            removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen());
228        }
229
230        AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this);
231
232        setupKeypressVibrationDurationSettings(prefs, res);
233        setupKeypressSoundVolumeSettings(prefs, res);
234        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
235    }
236
237    @Override
238    public void onResume() {
239        super.onResume();
240        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
241        final Resources res = getResources();
242        final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY);
243        if (voiceInputKeyOption != null) {
244            final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance()
245                    .isShortcutImeEnabled();
246            voiceInputKeyOption.setEnabled(isShortcutImeEnabled);
247            voiceInputKeyOption.setSummary(isShortcutImeEnabled ? null
248                    : res.getText(R.string.voice_input_disabled_summary));
249        }
250        final CheckBoxPreference showSetupWizardIcon =
251                (CheckBoxPreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON);
252        if (showSetupWizardIcon != null) {
253            showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity()));
254        }
255        updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
256        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
257        final ListPreference keyboardThemePref = (ListPreference)findPreference(
258                Settings.PREF_KEYBOARD_THEME);
259        if (keyboardThemePref != null) {
260            final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs);
261            final String value = Integer.toString(keyboardTheme.mThemeId);
262            final CharSequence entries[] = keyboardThemePref.getEntries();
263            final int entryIndex = keyboardThemePref.findIndexOfValue(value);
264            keyboardThemePref.setSummary(entryIndex < 0 ? null : entries[entryIndex]);
265            keyboardThemePref.setValue(value);
266        }
267        updateCustomInputStylesSummary(prefs, res);
268    }
269
270    @Override
271    public void onPause() {
272        super.onPause();
273        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
274        final ListPreference keyboardThemePref = (ListPreference)findPreference(
275                Settings.PREF_KEYBOARD_THEME);
276        if (keyboardThemePref != null) {
277            KeyboardTheme.saveKeyboardThemeId(keyboardThemePref.getValue(), prefs);
278        }
279    }
280
281    @Override
282    public void onDestroy() {
283        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
284                this);
285        super.onDestroy();
286    }
287
288    @Override
289    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
290        final Activity activity = getActivity();
291        if (activity == null) {
292            // TODO: Introduce a static function to register this class and ensure that
293            // onCreate must be called before "onSharedPreferenceChanged" is called.
294            Log.w(TAG, "onSharedPreferenceChanged called before activity starts.");
295            return;
296        }
297        (new BackupManager(activity)).dataChanged();
298        final Resources res = getResources();
299        if (key.equals(Settings.PREF_POPUP_ON)) {
300            setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
301                    Settings.readKeyPreviewPopupEnabled(prefs, res));
302        } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
303            setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
304                    Settings.readShowsLanguageSwitchKey(prefs));
305        } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) {
306            LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity());
307        }
308        ensureConsistencyOfAutoCorrectionSettings();
309        updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
310        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
311        updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME);
312        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
313    }
314
315    private void ensureConsistencyOfAutoCorrectionSettings() {
316        final String autoCorrectionOff = getResources().getString(
317                R.string.auto_correction_threshold_mode_index_off);
318        final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference(
319                Settings.PREF_AUTO_CORRECTION_THRESHOLD);
320        final String currentSetting = autoCorrectionThresholdPref.getValue();
321        setPreferenceEnabled(
322                Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff));
323    }
324
325    private void updateCustomInputStylesSummary(final SharedPreferences prefs,
326            final Resources res) {
327        final PreferenceScreen customInputStyles =
328                (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES);
329        final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
330        final InputMethodSubtype[] subtypes =
331                AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
332        final StringBuilder styles = new StringBuilder();
333        for (final InputMethodSubtype subtype : subtypes) {
334            if (styles.length() > 0) styles.append(", ");
335            styles.append(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
336        }
337        customInputStyles.setSummary(styles);
338    }
339
340    private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
341            final SharedPreferences sp, final Resources res) {
342        setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
343                Settings.readVibrationEnabled(sp, res));
344        setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME,
345                Settings.readKeypressSoundEnabled(sp, res));
346    }
347
348    private void setupKeypressVibrationDurationSettings(final SharedPreferences sp,
349            final Resources res) {
350        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
351                Settings.PREF_VIBRATION_DURATION_SETTINGS);
352        if (pref == null) {
353            return;
354        }
355        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
356            @Override
357            public void writeValue(final int value, final String key) {
358                sp.edit().putInt(key, value).apply();
359            }
360
361            @Override
362            public void writeDefaultValue(final String key) {
363                sp.edit().remove(key).apply();
364            }
365
366            @Override
367            public int readValue(final String key) {
368                return Settings.readKeypressVibrationDuration(sp, res);
369            }
370
371            @Override
372            public int readDefaultValue(final String key) {
373                return Settings.readDefaultKeypressVibrationDuration(res);
374            }
375
376            @Override
377            public void feedbackValue(final int value) {
378                AudioAndHapticFeedbackManager.getInstance().vibrate(value);
379            }
380
381            @Override
382            public String getValueText(final int value) {
383                if (value < 0) {
384                    return res.getString(R.string.settings_system_default);
385                }
386                return res.getString(R.string.abbreviation_unit_milliseconds, value);
387            }
388        });
389    }
390
391    private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) {
392        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
393                Settings.PREF_KEYPRESS_SOUND_VOLUME);
394        if (pref == null) {
395            return;
396        }
397        final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE);
398        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
399            private static final float PERCENTAGE_FLOAT = 100.0f;
400
401            private float getValueFromPercentage(final int percentage) {
402                return percentage / PERCENTAGE_FLOAT;
403            }
404
405            private int getPercentageFromValue(final float floatValue) {
406                return (int)(floatValue * PERCENTAGE_FLOAT);
407            }
408
409            @Override
410            public void writeValue(final int value, final String key) {
411                sp.edit().putFloat(key, getValueFromPercentage(value)).apply();
412            }
413
414            @Override
415            public void writeDefaultValue(final String key) {
416                sp.edit().remove(key).apply();
417            }
418
419            @Override
420            public int readValue(final String key) {
421                return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res));
422            }
423
424            @Override
425            public int readDefaultValue(final String key) {
426                return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res));
427            }
428
429            @Override
430            public String getValueText(final int value) {
431                if (value < 0) {
432                    return res.getString(R.string.settings_system_default);
433                }
434                return Integer.toString(value);
435            }
436
437            @Override
438            public void feedbackValue(final int value) {
439                am.playSoundEffect(
440                        AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value));
441            }
442        });
443    }
444
445    private void overwriteUserDictionaryPreference(Preference userDictionaryPreference) {
446        final Activity activity = getActivity();
447        final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity);
448        if (null == localeList) {
449            // The locale list is null if and only if the user dictionary service is
450            // not present or disabled. In this case we need to remove the preference.
451            getPreferenceScreen().removePreference(userDictionaryPreference);
452        } else if (localeList.size() <= 1) {
453            userDictionaryPreference.setFragment(UserDictionarySettings.class.getName());
454            // If the size of localeList is 0, we don't set the locale parameter in the
455            // extras. This will be interpreted by the UserDictionarySettings class as
456            // meaning "the current locale".
457            // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet()
458            // the locale list always has at least one element, since it always includes the current
459            // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet().
460            if (localeList.size() == 1) {
461                final String locale = (String)localeList.toArray()[0];
462                userDictionaryPreference.getExtras().putString("locale", locale);
463            }
464        } else {
465            userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
466        }
467    }
468}
469