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