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