1 /* 2 * Copyright (C) 2011 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.settings.inputmethod; 18 19 import android.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.content.DialogInterface; 22 import android.content.pm.ApplicationInfo; 23 import android.os.Bundle; 24 import android.provider.Settings; 25 import android.util.Log; 26 import android.view.textservice.SpellCheckerInfo; 27 import android.view.textservice.SpellCheckerSubtype; 28 import android.view.textservice.TextServicesManager; 29 import android.widget.Switch; 30 31 import androidx.appcompat.app.AlertDialog; 32 import androidx.preference.Preference; 33 import androidx.preference.Preference.OnPreferenceChangeListener; 34 import androidx.preference.PreferenceScreen; 35 36 import com.android.settings.R; 37 import com.android.settings.SettingsActivity; 38 import com.android.settings.SettingsPreferenceFragment; 39 import com.android.settings.widget.SwitchBar; 40 import com.android.settings.widget.SwitchBar.OnSwitchChangeListener; 41 42 public class SpellCheckersSettings extends SettingsPreferenceFragment 43 implements OnSwitchChangeListener, OnPreferenceChangeListener { 44 private static final String TAG = SpellCheckersSettings.class.getSimpleName(); 45 private static final boolean DBG = false; 46 47 private static final String KEY_SPELL_CHECKER_LANGUAGE = "spellchecker_language"; 48 private static final String KEY_DEFAULT_SPELL_CHECKER = "default_spellchecker"; 49 private static final int ITEM_ID_USE_SYSTEM_LANGUAGE = 0; 50 51 private SwitchBar mSwitchBar; 52 private Preference mSpellCheckerLanaguagePref; 53 private AlertDialog mDialog = null; 54 private SpellCheckerInfo mCurrentSci; 55 private SpellCheckerInfo[] mEnabledScis; 56 private TextServicesManager mTsm; 57 58 @Override getMetricsCategory()59 public int getMetricsCategory() { 60 return SettingsEnums.INPUTMETHOD_SPELL_CHECKERS; 61 } 62 63 @Override onCreate(final Bundle icicle)64 public void onCreate(final Bundle icicle) { 65 super.onCreate(icicle); 66 67 addPreferencesFromResource(R.xml.spellchecker_prefs); 68 mSpellCheckerLanaguagePref = findPreference(KEY_SPELL_CHECKER_LANGUAGE); 69 70 mTsm = (TextServicesManager) getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); 71 mCurrentSci = mTsm.getCurrentSpellChecker(); 72 mEnabledScis = mTsm.getEnabledSpellCheckers(); 73 populatePreferenceScreen(); 74 } 75 populatePreferenceScreen()76 private void populatePreferenceScreen() { 77 final SpellCheckerPreference pref = new SpellCheckerPreference(getPrefContext(), 78 mEnabledScis); 79 pref.setTitle(R.string.default_spell_checker); 80 final int count = (mEnabledScis == null) ? 0 : mEnabledScis.length; 81 if (count > 0) { 82 pref.setSummary("%s"); 83 } else { 84 pref.setSummary(R.string.spell_checker_not_selected); 85 } 86 pref.setKey(KEY_DEFAULT_SPELL_CHECKER); 87 pref.setOnPreferenceChangeListener(this); 88 getPreferenceScreen().addPreference(pref); 89 } 90 91 @Override onResume()92 public void onResume() { 93 super.onResume(); 94 mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar(); 95 mSwitchBar.setSwitchBarText( 96 R.string.spell_checker_master_switch_title, 97 R.string.spell_checker_master_switch_title); 98 mSwitchBar.show(); 99 mSwitchBar.addOnSwitchChangeListener(this); 100 updatePreferenceScreen(); 101 } 102 103 @Override onPause()104 public void onPause() { 105 super.onPause(); 106 mSwitchBar.removeOnSwitchChangeListener(this); 107 } 108 109 @Override onSwitchChanged(final Switch switchView, final boolean isChecked)110 public void onSwitchChanged(final Switch switchView, final boolean isChecked) { 111 Settings.Secure.putInt(getContentResolver(), Settings.Secure.SPELL_CHECKER_ENABLED, 112 isChecked ? 1 : 0); 113 updatePreferenceScreen(); 114 } 115 updatePreferenceScreen()116 private void updatePreferenceScreen() { 117 mCurrentSci = mTsm.getCurrentSpellChecker(); 118 final boolean isSpellCheckerEnabled = mTsm.isSpellCheckerEnabled(); 119 mSwitchBar.setChecked(isSpellCheckerEnabled); 120 121 final SpellCheckerSubtype currentScs; 122 if (mCurrentSci != null) { 123 currentScs = mTsm.getCurrentSpellCheckerSubtype( 124 false /* allowImplicitlySelectedSubtype */); 125 } else { 126 currentScs = null; 127 } 128 mSpellCheckerLanaguagePref.setSummary(getSpellCheckerSubtypeLabel(mCurrentSci, currentScs)); 129 130 final PreferenceScreen screen = getPreferenceScreen(); 131 final int count = screen.getPreferenceCount(); 132 for (int index = 0; index < count; index++) { 133 final Preference preference = screen.getPreference(index); 134 preference.setEnabled(isSpellCheckerEnabled); 135 if (preference instanceof SpellCheckerPreference) { 136 final SpellCheckerPreference pref = (SpellCheckerPreference) preference; 137 pref.setSelected(mCurrentSci); 138 } 139 } 140 mSpellCheckerLanaguagePref.setEnabled(isSpellCheckerEnabled && mCurrentSci != null); 141 } 142 getSpellCheckerSubtypeLabel(final SpellCheckerInfo sci, final SpellCheckerSubtype subtype)143 private CharSequence getSpellCheckerSubtypeLabel(final SpellCheckerInfo sci, 144 final SpellCheckerSubtype subtype) { 145 if (sci == null) { 146 return getString(R.string.spell_checker_not_selected); 147 } 148 if (subtype == null) { 149 return getString(R.string.use_system_language_to_select_input_method_subtypes); 150 } 151 return subtype.getDisplayName( 152 getActivity(), sci.getPackageName(), sci.getServiceInfo().applicationInfo); 153 } 154 155 @Override onPreferenceTreeClick(Preference preference)156 public boolean onPreferenceTreeClick(Preference preference) { 157 if (KEY_SPELL_CHECKER_LANGUAGE.equals(preference.getKey())) { 158 writePreferenceClickMetric(preference); 159 showChooseLanguageDialog(); 160 return true; 161 } 162 return super.onPreferenceTreeClick(preference); 163 } 164 165 @Override onPreferenceChange(Preference preference, Object newValue)166 public boolean onPreferenceChange(Preference preference, Object newValue) { 167 final SpellCheckerInfo sci = (SpellCheckerInfo) newValue; 168 final boolean isSystemApp = 169 (sci.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 170 if (isSystemApp) { 171 changeCurrentSpellChecker(sci); 172 return true; 173 } else { 174 showSecurityWarnDialog(sci); 175 return false; 176 } 177 } 178 convertSubtypeIndexToDialogItemId(final int index)179 private static int convertSubtypeIndexToDialogItemId(final int index) { 180 return index + 1; 181 } 182 convertDialogItemIdToSubtypeIndex(final int item)183 private static int convertDialogItemIdToSubtypeIndex(final int item) { 184 return item - 1; 185 } 186 showChooseLanguageDialog()187 private void showChooseLanguageDialog() { 188 if (mDialog != null && mDialog.isShowing()) { 189 mDialog.dismiss(); 190 } 191 final SpellCheckerInfo currentSci = mTsm.getCurrentSpellChecker(); 192 if (currentSci == null) { 193 // This can happen in some situations. One example is that the package that the current 194 // spell checker belongs to was uninstalled or being in background. 195 return; 196 } 197 final SpellCheckerSubtype currentScs = mTsm.getCurrentSpellCheckerSubtype( 198 false /* allowImplicitlySelectedSubtype */); 199 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 200 builder.setTitle(R.string.phone_language); 201 final int subtypeCount = currentSci.getSubtypeCount(); 202 final CharSequence[] items = new CharSequence[subtypeCount + 1 /* default */]; 203 items[ITEM_ID_USE_SYSTEM_LANGUAGE] = getSpellCheckerSubtypeLabel(currentSci, null); 204 int checkedItemId = ITEM_ID_USE_SYSTEM_LANGUAGE; 205 for (int index = 0; index < subtypeCount; ++index) { 206 final SpellCheckerSubtype subtype = currentSci.getSubtypeAt(index); 207 final int itemId = convertSubtypeIndexToDialogItemId(index); 208 items[itemId] = getSpellCheckerSubtypeLabel(currentSci, subtype); 209 if (subtype.equals(currentScs)) { 210 checkedItemId = itemId; 211 } 212 } 213 builder.setSingleChoiceItems(items, checkedItemId, new AlertDialog.OnClickListener() { 214 @Override 215 public void onClick(final DialogInterface dialog, final int item) { 216 final int subtypeId; 217 if (item == ITEM_ID_USE_SYSTEM_LANGUAGE) { 218 subtypeId = SpellCheckerSubtype.SUBTYPE_ID_NONE; 219 } else { 220 final int index = convertDialogItemIdToSubtypeIndex(item); 221 subtypeId = currentSci.getSubtypeAt(index).hashCode(); 222 } 223 224 Settings.Secure.putInt(getContentResolver(), 225 Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, subtypeId); 226 227 if (DBG) { 228 final SpellCheckerSubtype subtype = mTsm.getCurrentSpellCheckerSubtype( 229 true /* allowImplicitlySelectedSubtype */); 230 Log.d(TAG, "Current spell check locale is " 231 + subtype == null ? "null" : subtype.getLocale()); 232 } 233 dialog.dismiss(); 234 updatePreferenceScreen(); 235 } 236 }); 237 mDialog = builder.create(); 238 mDialog.show(); 239 } 240 showSecurityWarnDialog(final SpellCheckerInfo sci)241 private void showSecurityWarnDialog(final SpellCheckerInfo sci) { 242 if (mDialog != null && mDialog.isShowing()) { 243 mDialog.dismiss(); 244 } 245 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 246 builder.setTitle(android.R.string.dialog_alert_title); 247 builder.setMessage(getString(R.string.spellchecker_security_warning, 248 sci.loadLabel(getPackageManager()))); 249 builder.setCancelable(true); 250 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 251 @Override 252 public void onClick(final DialogInterface dialog, final int which) { 253 changeCurrentSpellChecker(sci); 254 } 255 }); 256 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 257 @Override 258 public void onClick(final DialogInterface dialog, final int which) { 259 } 260 }); 261 mDialog = builder.create(); 262 mDialog.show(); 263 } 264 changeCurrentSpellChecker(final SpellCheckerInfo sci)265 private void changeCurrentSpellChecker(final SpellCheckerInfo sci) { 266 Settings.Secure.putString(getContentResolver(), Settings.Secure.SELECTED_SPELL_CHECKER, 267 sci.getId()); 268 // Reset the spell checker subtype 269 Settings.Secure.putInt(getContentResolver(), Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, 270 SpellCheckerSubtype.SUBTYPE_ID_NONE); 271 if (DBG) { 272 Log.d(TAG, "Current spell check is " + mTsm.getCurrentSpellChecker().getId()); 273 } 274 updatePreferenceScreen(); 275 } 276 } 277