Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.Fragment;
     23 import android.app.backup.BackupManager;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.SharedPreferences;
     28 import android.content.res.Resources;
     29 import android.media.AudioManager;
     30 import android.os.Bundle;
     31 import android.preference.CheckBoxPreference;
     32 import android.preference.ListPreference;
     33 import android.preference.Preference;
     34 import android.preference.Preference.OnPreferenceClickListener;
     35 import android.preference.PreferenceGroup;
     36 import android.preference.PreferenceScreen;
     37 import android.text.TextUtils;
     38 import android.text.method.LinkMovementMethod;
     39 import android.util.Log;
     40 import android.view.View;
     41 import android.view.inputmethod.EditorInfo;
     42 import android.widget.SeekBar;
     43 import android.widget.SeekBar.OnSeekBarChangeListener;
     44 import android.widget.TextView;
     45 
     46 import com.android.inputmethod.compat.CompatUtils;
     47 import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
     48 import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
     49 import com.android.inputmethod.compat.InputTypeCompatUtils;
     50 import com.android.inputmethod.compat.VibratorCompatWrapper;
     51 import com.android.inputmethod.deprecated.VoiceProxy;
     52 import com.android.inputmethodcommon.InputMethodSettingsActivity;
     53 
     54 import java.util.Arrays;
     55 import java.util.Locale;
     56 
     57 public class Settings extends InputMethodSettingsActivity
     58         implements SharedPreferences.OnSharedPreferenceChangeListener,
     59         DialogInterface.OnDismissListener, OnPreferenceClickListener {
     60     private static final String TAG = Settings.class.getSimpleName();
     61 
     62     public static final boolean ENABLE_EXPERIMENTAL_SETTINGS = false;
     63 
     64     public static final String PREF_GENERAL_SETTINGS_KEY = "general_settings";
     65     public static final String PREF_VIBRATE_ON = "vibrate_on";
     66     public static final String PREF_SOUND_ON = "sound_on";
     67     public static final String PREF_KEY_PREVIEW_POPUP_ON = "popup_on";
     68     public static final String PREF_AUTO_CAP = "auto_cap";
     69     public static final String PREF_SHOW_SETTINGS_KEY = "show_settings_key";
     70     public static final String PREF_VOICE_SETTINGS_KEY = "voice_mode";
     71     public static final String PREF_INPUT_LANGUAGE = "input_language";
     72     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
     73     public static final String PREF_SUBTYPES = "subtype_settings";
     74 
     75     public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
     76     public static final String PREF_CORRECTION_SETTINGS_KEY = "correction_settings";
     77     public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
     78     public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
     79     public static final String PREF_DEBUG_SETTINGS = "debug_settings";
     80 
     81     public static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
     82     public static final String PREF_BIGRAM_PREDICTIONS = "bigram_prediction";
     83 
     84     public static final String PREF_MISC_SETTINGS_KEY = "misc_settings";
     85 
     86     public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
     87             "pref_key_preview_popup_dismiss_delay";
     88     public static final String PREF_KEY_USE_CONTACTS_DICT =
     89             "pref_key_use_contacts_dict";
     90     public static final String PREF_KEY_ENABLE_SPAN_INSERT =
     91             "enable_span_insert";
     92 
     93     public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
     94 
     95     public static final String PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS =
     96             "pref_vibration_duration_settings";
     97 
     98     public static final String PREF_KEYPRESS_SOUND_VOLUME =
     99             "pref_keypress_sound_volume";
    100     // Dialog ids
    101     private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
    102 
    103     public static class Values {
    104         // From resources:
    105         public final int mDelayUpdateOldSuggestions;
    106         public final String mWordSeparators;
    107         public final String mMagicSpaceStrippers;
    108         public final String mMagicSpaceSwappers;
    109         public final String mSuggestPuncs;
    110         public final SuggestedWords mSuggestPuncList;
    111         private final String mSymbolsExcludedFromWordSeparators;
    112 
    113         // From preferences:
    114         public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn)
    115         public final boolean mVibrateOn;
    116         public final boolean mKeyPreviewPopupOn;
    117         public final int mKeyPreviewPopupDismissDelay;
    118         public final boolean mAutoCap;
    119         public final boolean mAutoCorrectEnabled;
    120         public final double mAutoCorrectionThreshold;
    121         // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
    122         public final boolean mBigramSuggestionEnabled;
    123         // Prediction: use bigrams to predict the next word when there is no input for it yet
    124         public final boolean mBigramPredictionEnabled;
    125         public final boolean mUseContactsDict;
    126         public final boolean mEnableSuggestionSpanInsertion;
    127 
    128         private final boolean mShowSettingsKey;
    129         private final boolean mVoiceKeyEnabled;
    130         private final boolean mVoiceKeyOnMain;
    131 
    132         public Values(final SharedPreferences prefs, final Context context,
    133                 final String localeStr) {
    134             final Resources res = context.getResources();
    135             final Locale savedLocale;
    136             if (null != localeStr) {
    137                 final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr);
    138                 savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale);
    139             } else {
    140                 savedLocale = null;
    141             }
    142 
    143             // Get the resources
    144             mDelayUpdateOldSuggestions = res.getInteger(
    145                     R.integer.config_delay_update_old_suggestions);
    146             mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols);
    147             mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols);
    148             String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers
    149                     + res.getString(R.string.magic_space_promoting_symbols);
    150             final String symbolsExcludedFromWordSeparators =
    151                     res.getString(R.string.symbols_excluded_from_word_separators);
    152             for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) {
    153                 wordSeparators = wordSeparators.replace(
    154                         symbolsExcludedFromWordSeparators.substring(i, i + 1), "");
    155             }
    156             mSymbolsExcludedFromWordSeparators = symbolsExcludedFromWordSeparators;
    157             mWordSeparators = wordSeparators;
    158             mSuggestPuncs = res.getString(R.string.suggested_punctuations);
    159             // TODO: it would be nice not to recreate this each time we change the configuration
    160             mSuggestPuncList = createSuggestPuncList(mSuggestPuncs);
    161 
    162             // Get the settings preferences
    163             final boolean hasVibrator = VibratorCompatWrapper.getInstance(context).hasVibrator();
    164             mVibrateOn = hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON,
    165                     res.getBoolean(R.bool.config_default_vibration_enabled));
    166             mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
    167                     res.getBoolean(R.bool.config_default_sound_enabled));
    168             mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
    169             mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res);
    170             mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
    171             mAutoCorrectEnabled = isAutoCorrectEnabled(prefs, res);
    172             mBigramSuggestionEnabled = mAutoCorrectEnabled
    173                     && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
    174             mBigramPredictionEnabled = mBigramSuggestionEnabled
    175                     && isBigramPredictionEnabled(prefs, res);
    176             mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res);
    177             mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
    178             mEnableSuggestionSpanInsertion =
    179                     prefs.getBoolean(Settings.PREF_KEY_ENABLE_SPAN_INSERT, true);
    180             final boolean defaultShowSettingsKey = res.getBoolean(
    181                     R.bool.config_default_show_settings_key);
    182             mShowSettingsKey = isShowSettingsKeyOption(res)
    183                     ? prefs.getBoolean(Settings.PREF_SHOW_SETTINGS_KEY, defaultShowSettingsKey)
    184                     : defaultShowSettingsKey;
    185             final String voiceModeMain = res.getString(R.string.voice_mode_main);
    186             final String voiceModeOff = res.getString(R.string.voice_mode_off);
    187             final String voiceMode = prefs.getString(PREF_VOICE_SETTINGS_KEY, voiceModeMain);
    188             mVoiceKeyEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff);
    189             mVoiceKeyOnMain = voiceMode != null && voiceMode.equals(voiceModeMain);
    190 
    191             LocaleUtils.setSystemLocale(res, savedLocale);
    192         }
    193 
    194         public boolean isSuggestedPunctuation(int code) {
    195             return mSuggestPuncs.contains(String.valueOf((char)code));
    196         }
    197 
    198         public boolean isWordSeparator(int code) {
    199             return mWordSeparators.contains(String.valueOf((char)code));
    200         }
    201 
    202         public boolean isSymbolExcludedFromWordSeparators(int code) {
    203             return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
    204         }
    205 
    206         public boolean isMagicSpaceStripper(int code) {
    207             return mMagicSpaceStrippers.contains(String.valueOf((char)code));
    208         }
    209 
    210         public boolean isMagicSpaceSwapper(int code) {
    211             return mMagicSpaceSwappers.contains(String.valueOf((char)code));
    212         }
    213 
    214         private static boolean isAutoCorrectEnabled(SharedPreferences sp, Resources resources) {
    215             final String currentAutoCorrectionSetting = sp.getString(
    216                     Settings.PREF_AUTO_CORRECTION_THRESHOLD,
    217                     resources.getString(R.string.auto_correction_threshold_mode_index_modest));
    218             final String autoCorrectionOff = resources.getString(
    219                     R.string.auto_correction_threshold_mode_index_off);
    220             return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
    221         }
    222 
    223         // Public to access from KeyboardSwitcher. Should it have access to some
    224         // process-global instance instead?
    225         public static boolean isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources) {
    226             final boolean showPopupOption = resources.getBoolean(
    227                     R.bool.config_enable_show_popup_on_keypress_option);
    228             if (!showPopupOption) return resources.getBoolean(R.bool.config_default_popup_preview);
    229             return sp.getBoolean(Settings.PREF_KEY_PREVIEW_POPUP_ON,
    230                     resources.getBoolean(R.bool.config_default_popup_preview));
    231         }
    232 
    233         // Likewise
    234         public static int getKeyPreviewPopupDismissDelay(SharedPreferences sp,
    235                 Resources resources) {
    236             return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
    237                     Integer.toString(resources.getInteger(R.integer.config_delay_after_preview))));
    238         }
    239 
    240         private static boolean isBigramSuggestionEnabled(SharedPreferences sp, Resources resources,
    241                 boolean autoCorrectEnabled) {
    242             final boolean showBigramSuggestionsOption = resources.getBoolean(
    243                     R.bool.config_enable_bigram_suggestions_option);
    244             if (!showBigramSuggestionsOption) {
    245                 return autoCorrectEnabled;
    246             }
    247             return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, resources.getBoolean(
    248                     R.bool.config_default_bigram_suggestions));
    249         }
    250 
    251         private static boolean isBigramPredictionEnabled(SharedPreferences sp,
    252                 Resources resources) {
    253             return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean(
    254                     R.bool.config_default_bigram_prediction));
    255         }
    256 
    257         private static double getAutoCorrectionThreshold(SharedPreferences sp,
    258                 Resources resources) {
    259             final String currentAutoCorrectionSetting = sp.getString(
    260                     Settings.PREF_AUTO_CORRECTION_THRESHOLD,
    261                     resources.getString(R.string.auto_correction_threshold_mode_index_modest));
    262             final String[] autoCorrectionThresholdValues = resources.getStringArray(
    263                     R.array.auto_correction_threshold_values);
    264             // When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
    265             double autoCorrectionThreshold = Double.MAX_VALUE;
    266             try {
    267                 final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
    268                 if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
    269                     autoCorrectionThreshold = Double.parseDouble(
    270                             autoCorrectionThresholdValues[arrayIndex]);
    271                 }
    272             } catch (NumberFormatException e) {
    273                 // Whenever the threshold settings are correct, never come here.
    274                 autoCorrectionThreshold = Double.MAX_VALUE;
    275                 Log.w(TAG, "Cannot load auto correction threshold setting."
    276                         + " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
    277                         + ", autoCorrectionThresholdValues: "
    278                         + Arrays.toString(autoCorrectionThresholdValues));
    279             }
    280             return autoCorrectionThreshold;
    281         }
    282 
    283         private static SuggestedWords createSuggestPuncList(final String puncs) {
    284             SuggestedWords.Builder builder = new SuggestedWords.Builder();
    285             if (puncs != null) {
    286                 for (int i = 0; i < puncs.length(); i++) {
    287                     builder.addWord(puncs.subSequence(i, i + 1));
    288                 }
    289             }
    290             return builder.setIsPunctuationSuggestions().build();
    291         }
    292 
    293         public static boolean isShowSettingsKeyOption(final Resources resources) {
    294             return resources.getBoolean(R.bool.config_enable_show_settings_key_option);
    295 
    296         }
    297 
    298         public boolean isSettingsKeyEnabled() {
    299             return mShowSettingsKey;
    300         }
    301 
    302         public boolean isVoiceKeyEnabled(EditorInfo attribute) {
    303             final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
    304             final int inputType = (attribute != null) ? attribute.inputType : 0;
    305             return shortcutImeEnabled && mVoiceKeyEnabled
    306                     && !InputTypeCompatUtils.isPasswordInputType(inputType);
    307         }
    308 
    309         public boolean isVoiceKeyOnMain() {
    310             return mVoiceKeyOnMain;
    311         }
    312     }
    313 
    314     private PreferenceScreen mInputLanguageSelection;
    315     private PreferenceScreen mKeypressVibrationDurationSettingsPref;
    316     private PreferenceScreen mKeypressSoundVolumeSettingsPref;
    317     private ListPreference mVoicePreference;
    318     private CheckBoxPreference mShowSettingsKeyPreference;
    319     private ListPreference mShowCorrectionSuggestionsPreference;
    320     private ListPreference mAutoCorrectionThresholdPreference;
    321     private ListPreference mKeyPreviewPopupDismissDelay;
    322     // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
    323     private CheckBoxPreference mBigramSuggestion;
    324     // Prediction: use bigrams to predict the next word when there is no input for it yet
    325     private CheckBoxPreference mBigramPrediction;
    326     private Preference mDebugSettingsPreference;
    327     private boolean mVoiceOn;
    328 
    329     private AlertDialog mDialog;
    330     private TextView mKeypressVibrationDurationSettingsTextView;
    331     private TextView mKeypressSoundVolumeSettingsTextView;
    332 
    333     private boolean mOkClicked = false;
    334     private String mVoiceModeOff;
    335 
    336     private void ensureConsistencyOfAutoCorrectionSettings() {
    337         final String autoCorrectionOff = getResources().getString(
    338                 R.string.auto_correction_threshold_mode_index_off);
    339         final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
    340         mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
    341         if (null != mBigramPrediction) {
    342             mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
    343         }
    344     }
    345 
    346     public Activity getActivityInternal() {
    347         Object thisObject = (Object) this;
    348         if (thisObject instanceof Activity) {
    349             return (Activity) thisObject;
    350         } else if (thisObject instanceof Fragment) {
    351             return ((Fragment) thisObject).getActivity();
    352         } else {
    353             return null;
    354         }
    355     }
    356 
    357     @Override
    358     public void onCreate(Bundle icicle) {
    359         super.onCreate(icicle);
    360         setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
    361         setSubtypeEnablerTitle(R.string.select_language);
    362         final Resources res = getResources();
    363         final Context context = getActivityInternal();
    364 
    365         addPreferencesFromResource(R.xml.prefs);
    366         mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES);
    367         mInputLanguageSelection.setOnPreferenceClickListener(this);
    368         mVoicePreference = (ListPreference) findPreference(PREF_VOICE_SETTINGS_KEY);
    369         mShowSettingsKeyPreference = (CheckBoxPreference) findPreference(PREF_SHOW_SETTINGS_KEY);
    370         mShowCorrectionSuggestionsPreference =
    371                 (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
    372         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
    373         prefs.registerOnSharedPreferenceChangeListener(this);
    374 
    375         mVoiceModeOff = getString(R.string.voice_mode_off);
    376         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
    377                 .equals(mVoiceModeOff));
    378 
    379         mAutoCorrectionThresholdPreference =
    380                 (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
    381         mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTIONS);
    382         mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
    383         mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
    384         if (mDebugSettingsPreference != null) {
    385             final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
    386             debugSettingsIntent.setClassName(
    387                     context.getPackageName(), DebugSettings.class.getName());
    388             mDebugSettingsPreference.setIntent(debugSettingsIntent);
    389         }
    390 
    391         ensureConsistencyOfAutoCorrectionSettings();
    392 
    393         final PreferenceGroup generalSettings =
    394                 (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS_KEY);
    395         final PreferenceGroup textCorrectionGroup =
    396                 (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS_KEY);
    397         final PreferenceGroup miscSettings =
    398                 (PreferenceGroup) findPreference(PREF_MISC_SETTINGS_KEY);
    399 
    400         if (!Values.isShowSettingsKeyOption(res)) {
    401             generalSettings.removePreference(mShowSettingsKeyPreference);
    402         }
    403 
    404         final boolean showVoiceKeyOption = res.getBoolean(
    405                 R.bool.config_enable_show_voice_key_option);
    406         if (!showVoiceKeyOption) {
    407             generalSettings.removePreference(mVoicePreference);
    408         }
    409 
    410         if (!VibratorCompatWrapper.getInstance(context).hasVibrator()) {
    411             generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
    412         }
    413 
    414         if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) {
    415             generalSettings.removePreference(findPreference(PREF_SUBTYPES));
    416         }
    417 
    418         final boolean showPopupOption = res.getBoolean(
    419                 R.bool.config_enable_show_popup_on_keypress_option);
    420         if (!showPopupOption) {
    421             generalSettings.removePreference(findPreference(PREF_KEY_PREVIEW_POPUP_ON));
    422         }
    423 
    424         final boolean showBigramSuggestionsOption = res.getBoolean(
    425                 R.bool.config_enable_bigram_suggestions_option);
    426         if (!showBigramSuggestionsOption) {
    427             textCorrectionGroup.removePreference(mBigramSuggestion);
    428             if (null != mBigramPrediction) {
    429                 textCorrectionGroup.removePreference(mBigramPrediction);
    430             }
    431         }
    432 
    433         mKeyPreviewPopupDismissDelay =
    434                 (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
    435         final String[] entries = new String[] {
    436                 res.getString(R.string.key_preview_popup_dismiss_no_delay),
    437                 res.getString(R.string.key_preview_popup_dismiss_default_delay),
    438         };
    439         final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
    440                 R.integer.config_delay_after_preview));
    441         mKeyPreviewPopupDismissDelay.setEntries(entries);
    442         mKeyPreviewPopupDismissDelay.setEntryValues(
    443                 new String[] { "0", popupDismissDelayDefaultValue });
    444         if (null == mKeyPreviewPopupDismissDelay.getValue()) {
    445             mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
    446         }
    447         mKeyPreviewPopupDismissDelay.setEnabled(Values.isKeyPreviewPopupEnabled(prefs, res));
    448 
    449         final PreferenceScreen dictionaryLink =
    450                 (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
    451         final Intent intent = dictionaryLink.getIntent();
    452 
    453         final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
    454         if (0 >= number) {
    455             textCorrectionGroup.removePreference(dictionaryLink);
    456         }
    457 
    458         final boolean showUsabilityModeStudyOption = res.getBoolean(
    459                 R.bool.config_enable_usability_study_mode_option);
    460         if (!showUsabilityModeStudyOption || !ENABLE_EXPERIMENTAL_SETTINGS) {
    461             final Preference pref = findPreference(PREF_USABILITY_STUDY_MODE);
    462             if (pref != null) {
    463                 miscSettings.removePreference(pref);
    464             }
    465         }
    466 
    467         mKeypressVibrationDurationSettingsPref =
    468                 (PreferenceScreen) findPreference(PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS);
    469         if (mKeypressVibrationDurationSettingsPref != null) {
    470             mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener(
    471                     new OnPreferenceClickListener() {
    472                         @Override
    473                         public boolean onPreferenceClick(Preference arg0) {
    474                             showKeypressVibrationDurationSettingsDialog();
    475                             return true;
    476                         }
    477                     });
    478             updateKeypressVibrationDurationSettingsSummary(prefs, res);
    479         }
    480 
    481         mKeypressSoundVolumeSettingsPref =
    482                 (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME);
    483         if (mKeypressSoundVolumeSettingsPref != null) {
    484             mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener(
    485                     new OnPreferenceClickListener() {
    486                         @Override
    487                         public boolean onPreferenceClick(Preference arg0) {
    488                             showKeypressSoundVolumeSettingDialog();
    489                             return true;
    490                         }
    491                     });
    492             updateKeypressSoundVolumeSummary(prefs, res);
    493         }
    494         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
    495     }
    496 
    497     @SuppressWarnings("unused")
    498     @Override
    499     public void onResume() {
    500         super.onResume();
    501         final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
    502         if (isShortcutImeEnabled
    503                 || (VoiceProxy.VOICE_INSTALLED
    504                         && VoiceProxy.isRecognitionAvailable(getActivityInternal()))) {
    505             updateVoiceModeSummary();
    506         } else {
    507             getPreferenceScreen().removePreference(mVoicePreference);
    508         }
    509         updateShowCorrectionSuggestionsSummary();
    510         updateKeyPreviewPopupDelaySummary();
    511     }
    512 
    513     @Override
    514     public void onDestroy() {
    515         getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
    516                 this);
    517         super.onDestroy();
    518     }
    519 
    520     @Override
    521     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    522         (new BackupManager(getActivityInternal())).dataChanged();
    523         // If turning on voice input, show dialog
    524         if (key.equals(PREF_VOICE_SETTINGS_KEY) && !mVoiceOn) {
    525             if (!prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
    526                     .equals(mVoiceModeOff)) {
    527                 showVoiceConfirmation();
    528             }
    529         } else if (key.equals(PREF_KEY_PREVIEW_POPUP_ON)) {
    530             final ListPreference popupDismissDelay =
    531                 (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
    532             if (null != popupDismissDelay) {
    533                 popupDismissDelay.setEnabled(prefs.getBoolean(PREF_KEY_PREVIEW_POPUP_ON, true));
    534             }
    535         }
    536         ensureConsistencyOfAutoCorrectionSettings();
    537         mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff)
    538                 .equals(mVoiceModeOff));
    539         updateVoiceModeSummary();
    540         updateShowCorrectionSuggestionsSummary();
    541         updateKeyPreviewPopupDelaySummary();
    542         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
    543     }
    544 
    545     @Override
    546     public boolean onPreferenceClick(Preference pref) {
    547         if (pref == mInputLanguageSelection) {
    548             startActivity(CompatUtils.getInputLanguageSelectionIntent(
    549                     Utils.getInputMethodId(
    550                             InputMethodManagerCompatWrapper.getInstance(),
    551                             getActivityInternal().getApplicationInfo().packageName), 0));
    552             return true;
    553         }
    554         return false;
    555     }
    556 
    557     private void updateShowCorrectionSuggestionsSummary() {
    558         mShowCorrectionSuggestionsPreference.setSummary(
    559                 getResources().getStringArray(R.array.prefs_suggestion_visibilities)
    560                 [mShowCorrectionSuggestionsPreference.findIndexOfValue(
    561                         mShowCorrectionSuggestionsPreference.getValue())]);
    562     }
    563 
    564     private void updateKeyPreviewPopupDelaySummary() {
    565         final ListPreference lp = mKeyPreviewPopupDismissDelay;
    566         lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
    567     }
    568 
    569     private void showVoiceConfirmation() {
    570         mOkClicked = false;
    571         getActivityInternal().showDialog(VOICE_INPUT_CONFIRM_DIALOG);
    572         // Make URL in the dialog message clickable
    573         if (mDialog != null) {
    574             TextView textView = (TextView) mDialog.findViewById(android.R.id.message);
    575             if (textView != null) {
    576                 textView.setMovementMethod(LinkMovementMethod.getInstance());
    577             }
    578         }
    579     }
    580 
    581     private void updateVoiceModeSummary() {
    582         mVoicePreference.setSummary(
    583                 getResources().getStringArray(R.array.voice_input_modes_summary)
    584                 [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
    585     }
    586 
    587     @Override
    588     protected Dialog onCreateDialog(int id) {
    589         switch (id) {
    590             case VOICE_INPUT_CONFIRM_DIALOG:
    591                 DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
    592                     @Override
    593                     public void onClick(DialogInterface dialog, int whichButton) {
    594                         if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
    595                             mVoicePreference.setValue(mVoiceModeOff);
    596                         } else if (whichButton == DialogInterface.BUTTON_POSITIVE) {
    597                             mOkClicked = true;
    598                         }
    599                     }
    600                 };
    601                 AlertDialog.Builder builder = new AlertDialog.Builder(getActivityInternal())
    602                         .setTitle(R.string.voice_warning_title)
    603                         .setPositiveButton(android.R.string.ok, listener)
    604                         .setNegativeButton(android.R.string.cancel, listener);
    605 
    606                 // Get the current list of supported locales and check the current locale against
    607                 // that list, to decide whether to put a warning that voice input will not work in
    608                 // the current language as part of the pop-up confirmation dialog.
    609                 boolean localeSupported = SubtypeSwitcher.isVoiceSupported(
    610                         this, Locale.getDefault().toString());
    611 
    612                 final CharSequence message;
    613                 if (localeSupported) {
    614                     message = TextUtils.concat(
    615                             getText(R.string.voice_warning_may_not_understand), "\n\n",
    616                                     getText(R.string.voice_hint_dialog_message));
    617                 } else {
    618                     message = TextUtils.concat(
    619                             getText(R.string.voice_warning_locale_not_supported), "\n\n",
    620                                     getText(R.string.voice_warning_may_not_understand), "\n\n",
    621                                             getText(R.string.voice_hint_dialog_message));
    622                 }
    623                 builder.setMessage(message);
    624                 AlertDialog dialog = builder.create();
    625                 mDialog = dialog;
    626                 dialog.setOnDismissListener(this);
    627                 return dialog;
    628             default:
    629                 Log.e(TAG, "unknown dialog " + id);
    630                 return null;
    631         }
    632     }
    633 
    634     @Override
    635     public void onDismiss(DialogInterface dialog) {
    636         if (!mOkClicked) {
    637             // This assumes that onPreferenceClick gets called first, and this if the user
    638             // agreed after the warning, we set the mOkClicked value to true.
    639             mVoicePreference.setValue(mVoiceModeOff);
    640         }
    641     }
    642 
    643     private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
    644             SharedPreferences sp, Resources res) {
    645         if (mKeypressVibrationDurationSettingsPref != null) {
    646             final boolean hasVibrator = VibratorCompatWrapper.getInstance(this).hasVibrator();
    647             final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON,
    648                     res.getBoolean(R.bool.config_default_vibration_enabled));
    649             mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn);
    650         }
    651 
    652         if (mKeypressSoundVolumeSettingsPref != null) {
    653             final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
    654                     res.getBoolean(R.bool.config_default_sound_enabled));
    655             mKeypressSoundVolumeSettingsPref.setEnabled(soundOn);
    656         }
    657     }
    658 
    659     private void updateKeypressVibrationDurationSettingsSummary(
    660             SharedPreferences sp, Resources res) {
    661         if (mKeypressVibrationDurationSettingsPref != null) {
    662             mKeypressVibrationDurationSettingsPref.setSummary(
    663                     Utils.getCurrentVibrationDuration(sp, res)
    664                             + res.getString(R.string.settings_ms));
    665         }
    666     }
    667 
    668     private void showKeypressVibrationDurationSettingsDialog() {
    669         final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
    670         final Activity context = getActivityInternal();
    671         final Resources res = context.getResources();
    672         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
    673         builder.setTitle(R.string.prefs_keypress_vibration_duration_settings);
    674         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    675             @Override
    676             public void onClick(DialogInterface dialog, int whichButton) {
    677                 final int ms = Integer.valueOf(
    678                         mKeypressVibrationDurationSettingsTextView.getText().toString());
    679                 sp.edit().putInt(Settings.PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS, ms).apply();
    680                 updateKeypressVibrationDurationSettingsSummary(sp, res);
    681             }
    682         });
    683         builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
    684             @Override
    685             public void onClick(DialogInterface dialog, int whichButton) {
    686                 dialog.dismiss();
    687             }
    688         });
    689         final View v = context.getLayoutInflater().inflate(
    690                 R.layout.vibration_settings_dialog, null);
    691         final int currentMs = Utils.getCurrentVibrationDuration(
    692                 getPreferenceManager().getSharedPreferences(), getResources());
    693         mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value);
    694         final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings);
    695         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    696             @Override
    697             public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
    698                 final int tempMs = arg1;
    699                 mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs));
    700             }
    701 
    702             @Override
    703             public void onStartTrackingTouch(SeekBar arg0) {
    704             }
    705 
    706             @Override
    707             public void onStopTrackingTouch(SeekBar arg0) {
    708                 final int tempMs = arg0.getProgress();
    709                 VibratorCompatWrapper.getInstance(context).vibrate(tempMs);
    710             }
    711         });
    712         sb.setProgress(currentMs);
    713         mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs));
    714         builder.setView(v);
    715         builder.create().show();
    716     }
    717 
    718     private void updateKeypressSoundVolumeSummary(SharedPreferences sp, Resources res) {
    719         if (mKeypressSoundVolumeSettingsPref != null) {
    720             mKeypressSoundVolumeSettingsPref.setSummary(
    721                     String.valueOf((int)(Utils.getCurrentKeypressSoundVolume(sp, res) * 100)));
    722         }
    723     }
    724 
    725     private void showKeypressSoundVolumeSettingDialog() {
    726         final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    727         final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
    728         final Activity context = getActivityInternal();
    729         final Resources res = context.getResources();
    730         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
    731         builder.setTitle(R.string.prefs_keypress_sound_volume_settings);
    732         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    733             @Override
    734             public void onClick(DialogInterface dialog, int whichButton) {
    735                 final float volume =
    736                         ((float)Integer.valueOf(
    737                                 mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100;
    738                 sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply();
    739                 updateKeypressSoundVolumeSummary(sp, res);
    740             }
    741         });
    742         builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
    743             @Override
    744             public void onClick(DialogInterface dialog, int whichButton) {
    745                 dialog.dismiss();
    746             }
    747         });
    748         final View v = context.getLayoutInflater().inflate(
    749                 R.layout.sound_effect_volume_dialog, null);
    750         final int currentVolumeInt = (int)(Utils.getCurrentKeypressSoundVolume(
    751                 getPreferenceManager().getSharedPreferences(), getResources()) * 100);
    752         mKeypressSoundVolumeSettingsTextView =
    753                 (TextView)v.findViewById(R.id.sound_effect_volume_value);
    754         final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar);
    755         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    756             @Override
    757             public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
    758                 final int tempVolume = arg1;
    759                 mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume));
    760             }
    761 
    762             @Override
    763             public void onStartTrackingTouch(SeekBar arg0) {
    764             }
    765 
    766             @Override
    767             public void onStopTrackingTouch(SeekBar arg0) {
    768                 final float tempVolume = ((float)arg0.getProgress()) / 100;
    769                 am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume);
    770             }
    771         });
    772         sb.setProgress(currentVolumeInt);
    773         mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt));
    774         builder.setView(v);
    775         builder.create().show();
    776     }
    777 }