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