• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.AlertDialog;
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.content.SharedPreferences;
25 import android.content.res.Resources;
26 import android.os.Bundle;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.preference.DialogPreference;
30 import android.preference.Preference;
31 import android.preference.PreferenceFragment;
32 import android.preference.PreferenceGroup;
33 import android.support.v4.view.ViewCompat;
34 import android.text.TextUtils;
35 import android.util.Pair;
36 import android.view.LayoutInflater;
37 import android.view.Menu;
38 import android.view.MenuInflater;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.view.inputmethod.InputMethodInfo;
43 import android.view.inputmethod.InputMethodSubtype;
44 import android.widget.ArrayAdapter;
45 import android.widget.Spinner;
46 import android.widget.SpinnerAdapter;
47 import android.widget.Toast;
48 
49 import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
50 import com.android.inputmethod.compat.ViewCompatUtils;
51 import com.android.inputmethod.latin.R;
52 import com.android.inputmethod.latin.RichInputMethodManager;
53 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
54 import com.android.inputmethod.latin.utils.DialogUtils;
55 import com.android.inputmethod.latin.utils.IntentUtils;
56 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
57 
58 import java.util.ArrayList;
59 import java.util.TreeSet;
60 
61 public final class CustomInputStyleSettingsFragment extends PreferenceFragment {
62     private RichInputMethodManager mRichImm;
63     private SharedPreferences mPrefs;
64     private SubtypeLocaleAdapter mSubtypeLocaleAdapter;
65     private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
66 
67     private boolean mIsAddingNewSubtype;
68     private AlertDialog mSubtypeEnablerNotificationDialog;
69     private String mSubtypePreferenceKeyForSubtypeEnabler;
70 
71     private static final String KEY_IS_ADDING_NEW_SUBTYPE = "is_adding_new_subtype";
72     private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
73             "is_subtype_enabler_notification_dialog_open";
74     private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
75 
76     static final class SubtypeLocaleItem extends Pair<String, String>
77             implements Comparable<SubtypeLocaleItem> {
SubtypeLocaleItem(final String localeString, final String displayName)78         public SubtypeLocaleItem(final String localeString, final String displayName) {
79             super(localeString, displayName);
80         }
81 
SubtypeLocaleItem(final String localeString)82         public SubtypeLocaleItem(final String localeString) {
83             this(localeString,
84                     SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(localeString));
85         }
86 
87         @Override
toString()88         public String toString() {
89             return second;
90         }
91 
92         @Override
compareTo(final SubtypeLocaleItem o)93         public int compareTo(final SubtypeLocaleItem o) {
94             return first.compareTo(o.first);
95         }
96     }
97 
98     static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
99         private static final String TAG = SubtypeLocaleAdapter.class.getSimpleName();
100         private static final boolean DEBUG_SUBTYPE_ID = false;
101 
SubtypeLocaleAdapter(final Context context)102         public SubtypeLocaleAdapter(final Context context) {
103             super(context, android.R.layout.simple_spinner_item);
104             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
105 
106             final TreeSet<SubtypeLocaleItem> items = new TreeSet<>();
107             final InputMethodInfo imi = RichInputMethodManager.getInstance()
108                     .getInputMethodInfoOfThisIme();
109             final int count = imi.getSubtypeCount();
110             for (int i = 0; i < count; i++) {
111                 final InputMethodSubtype subtype = imi.getSubtypeAt(i);
112                 if (DEBUG_SUBTYPE_ID) {
113                     android.util.Log.d(TAG, String.format("%-6s 0x%08x %11d %s",
114                             subtype.getLocale(), subtype.hashCode(), subtype.hashCode(),
115                             SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)));
116                 }
117                 if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) {
118                     items.add(createItem(context, subtype.getLocale()));
119                 }
120             }
121             // TODO: Should filter out already existing combinations of locale and layout.
122             addAll(items);
123         }
124 
createItem(final Context context, final String localeString)125         public static SubtypeLocaleItem createItem(final Context context,
126                 final String localeString) {
127             if (localeString.equals(SubtypeLocaleUtils.NO_LANGUAGE)) {
128                 final String displayName = context.getString(R.string.subtype_no_language);
129                 return new SubtypeLocaleItem(localeString, displayName);
130             }
131             return new SubtypeLocaleItem(localeString);
132         }
133     }
134 
135     static final class KeyboardLayoutSetItem extends Pair<String, String> {
KeyboardLayoutSetItem(final InputMethodSubtype subtype)136         public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
137             super(SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype),
138                     SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype));
139         }
140 
141         @Override
toString()142         public String toString() {
143             return second;
144         }
145     }
146 
147     static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
KeyboardLayoutSetAdapter(final Context context)148         public KeyboardLayoutSetAdapter(final Context context) {
149             super(context, android.R.layout.simple_spinner_item);
150             setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
151 
152             // TODO: Should filter out already existing combinations of locale and layout.
153             for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
154                 // This is a dummy subtype with NO_LANGUAGE, only for display.
155                 final InputMethodSubtype subtype =
156                         AdditionalSubtypeUtils.createDummyAdditionalSubtype(
157                                 SubtypeLocaleUtils.NO_LANGUAGE, layout);
158                 add(new KeyboardLayoutSetItem(subtype));
159             }
160         }
161     }
162 
163     private interface SubtypeDialogProxy {
onRemovePressed(SubtypePreference subtypePref)164         public void onRemovePressed(SubtypePreference subtypePref);
onSavePressed(SubtypePreference subtypePref)165         public void onSavePressed(SubtypePreference subtypePref);
onAddPressed(SubtypePreference subtypePref)166         public void onAddPressed(SubtypePreference subtypePref);
getSubtypeLocaleAdapter()167         public SubtypeLocaleAdapter getSubtypeLocaleAdapter();
getKeyboardLayoutSetAdapter()168         public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter();
169     }
170 
171     static final class SubtypePreference extends DialogPreference
172             implements DialogInterface.OnCancelListener {
173         private static final String KEY_PREFIX = "subtype_pref_";
174         private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new";
175 
176         private InputMethodSubtype mSubtype;
177         private InputMethodSubtype mPreviousSubtype;
178 
179         private final SubtypeDialogProxy mProxy;
180         private Spinner mSubtypeLocaleSpinner;
181         private Spinner mKeyboardLayoutSetSpinner;
182 
newIncompleteSubtypePreference(final Context context, final SubtypeDialogProxy proxy)183         public static SubtypePreference newIncompleteSubtypePreference(final Context context,
184                 final SubtypeDialogProxy proxy) {
185             return new SubtypePreference(context, null, proxy);
186         }
187 
SubtypePreference(final Context context, final InputMethodSubtype subtype, final SubtypeDialogProxy proxy)188         public SubtypePreference(final Context context, final InputMethodSubtype subtype,
189                 final SubtypeDialogProxy proxy) {
190             super(context, null);
191             setDialogLayoutResource(R.layout.additional_subtype_dialog);
192             setPersistent(false);
193             mProxy = proxy;
194             setSubtype(subtype);
195         }
196 
show()197         public void show() {
198             showDialog(null);
199         }
200 
isIncomplete()201         public final boolean isIncomplete() {
202             return mSubtype == null;
203         }
204 
getSubtype()205         public InputMethodSubtype getSubtype() {
206             return mSubtype;
207         }
208 
setSubtype(final InputMethodSubtype subtype)209         public void setSubtype(final InputMethodSubtype subtype) {
210             mPreviousSubtype = mSubtype;
211             mSubtype = subtype;
212             if (isIncomplete()) {
213                 setTitle(null);
214                 setDialogTitle(R.string.add_style);
215                 setKey(KEY_NEW_SUBTYPE);
216             } else {
217                 final String displayName =
218                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype);
219                 setTitle(displayName);
220                 setDialogTitle(displayName);
221                 setKey(KEY_PREFIX + subtype.getLocale() + "_"
222                         + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype));
223             }
224         }
225 
revert()226         public void revert() {
227             setSubtype(mPreviousSubtype);
228         }
229 
hasBeenModified()230         public boolean hasBeenModified() {
231             return mSubtype != null && !mSubtype.equals(mPreviousSubtype);
232         }
233 
234         @Override
onCreateDialogView()235         protected View onCreateDialogView() {
236             final View v = super.onCreateDialogView();
237             mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner);
238             mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter());
239             mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner);
240             mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter());
241             // All keyboard layout names are in the Latin script and thus left to right. That means
242             // the view would align them to the left even if the system locale is RTL, but that
243             // would look strange. To fix this, we align them to the view's start, which will be
244             // natural for any direction.
245             ViewCompatUtils.setTextAlignment(
246                     mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START);
247             return v;
248         }
249 
250         @Override
onPrepareDialogBuilder(final AlertDialog.Builder builder)251         protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
252             final Context context = builder.getContext();
253             builder.setCancelable(true).setOnCancelListener(this);
254             if (isIncomplete()) {
255                 builder.setPositiveButton(R.string.add, this)
256                         .setNegativeButton(android.R.string.cancel, this);
257             } else {
258                 builder.setPositiveButton(R.string.save, this)
259                         .setNeutralButton(android.R.string.cancel, this)
260                         .setNegativeButton(R.string.remove, this);
261                 final SubtypeLocaleItem localeItem = SubtypeLocaleAdapter.createItem(
262                         context, mSubtype.getLocale());
263                 final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
264                 setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
265                 setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
266             }
267         }
268 
setSpinnerPosition(final Spinner spinner, final Object itemToSelect)269         private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) {
270             final SpinnerAdapter adapter = spinner.getAdapter();
271             final int count = adapter.getCount();
272             for (int i = 0; i < count; i++) {
273                 final Object item = spinner.getItemAtPosition(i);
274                 if (item.equals(itemToSelect)) {
275                     spinner.setSelection(i);
276                     return;
277                 }
278             }
279         }
280 
281         @Override
onCancel(final DialogInterface dialog)282         public void onCancel(final DialogInterface dialog) {
283             if (isIncomplete()) {
284                 mProxy.onRemovePressed(this);
285             }
286         }
287 
288         @Override
onClick(final DialogInterface dialog, final int which)289         public void onClick(final DialogInterface dialog, final int which) {
290             super.onClick(dialog, which);
291             switch (which) {
292             case DialogInterface.BUTTON_POSITIVE:
293                 final boolean isEditing = !isIncomplete();
294                 final SubtypeLocaleItem locale =
295                         (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem();
296                 final KeyboardLayoutSetItem layout =
297                         (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem();
298                 final InputMethodSubtype subtype =
299                         AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype(
300                                 locale.first, layout.first);
301                 setSubtype(subtype);
302                 notifyChanged();
303                 if (isEditing) {
304                     mProxy.onSavePressed(this);
305                 } else {
306                     mProxy.onAddPressed(this);
307                 }
308                 break;
309             case DialogInterface.BUTTON_NEUTRAL:
310                 // Nothing to do
311                 break;
312             case DialogInterface.BUTTON_NEGATIVE:
313                 mProxy.onRemovePressed(this);
314                 break;
315             }
316         }
317 
getSpinnerPosition(final Spinner spinner)318         private static int getSpinnerPosition(final Spinner spinner) {
319             if (spinner == null) return -1;
320             return spinner.getSelectedItemPosition();
321         }
322 
setSpinnerPosition(final Spinner spinner, final int position)323         private static void setSpinnerPosition(final Spinner spinner, final int position) {
324             if (spinner == null || position < 0) return;
325             spinner.setSelection(position);
326         }
327 
328         @Override
onSaveInstanceState()329         protected Parcelable onSaveInstanceState() {
330             final Parcelable superState = super.onSaveInstanceState();
331             final Dialog dialog = getDialog();
332             if (dialog == null || !dialog.isShowing()) {
333                 return superState;
334             }
335 
336             final SavedState myState = new SavedState(superState);
337             myState.mSubtype = mSubtype;
338             myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner);
339             myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner);
340             return myState;
341         }
342 
343         @Override
onRestoreInstanceState(final Parcelable state)344         protected void onRestoreInstanceState(final Parcelable state) {
345             if (!(state instanceof SavedState)) {
346                 super.onRestoreInstanceState(state);
347                 return;
348             }
349 
350             final SavedState myState = (SavedState) state;
351             super.onRestoreInstanceState(myState.getSuperState());
352             setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos);
353             setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos);
354             setSubtype(myState.mSubtype);
355         }
356 
357         static final class SavedState extends Preference.BaseSavedState {
358             InputMethodSubtype mSubtype;
359             int mSubtypeLocaleSelectedPos;
360             int mKeyboardLayoutSetSelectedPos;
361 
SavedState(final Parcelable superState)362             public SavedState(final Parcelable superState) {
363                 super(superState);
364             }
365 
366             @Override
writeToParcel(final Parcel dest, final int flags)367             public void writeToParcel(final Parcel dest, final int flags) {
368                 super.writeToParcel(dest, flags);
369                 dest.writeInt(mSubtypeLocaleSelectedPos);
370                 dest.writeInt(mKeyboardLayoutSetSelectedPos);
371                 dest.writeParcelable(mSubtype, 0);
372             }
373 
SavedState(final Parcel source)374             public SavedState(final Parcel source) {
375                 super(source);
376                 mSubtypeLocaleSelectedPos = source.readInt();
377                 mKeyboardLayoutSetSelectedPos = source.readInt();
378                 mSubtype = (InputMethodSubtype)source.readParcelable(null);
379             }
380 
381             public static final Parcelable.Creator<SavedState> CREATOR =
382                     new Parcelable.Creator<SavedState>() {
383                         @Override
384                         public SavedState createFromParcel(final Parcel source) {
385                             return new SavedState(source);
386                         }
387 
388                         @Override
389                         public SavedState[] newArray(final int size) {
390                             return new SavedState[size];
391                         }
392                     };
393         }
394     }
395 
CustomInputStyleSettingsFragment()396     public CustomInputStyleSettingsFragment() {
397         // Empty constructor for fragment generation.
398     }
399 
updateCustomInputStylesSummary(final Preference pref)400     static void updateCustomInputStylesSummary(final Preference pref) {
401         // When we are called from the Settings application but we are not already running, some
402         // singleton and utility classes may not have been initialized.  We have to call
403         // initialization method of these classes here. See {@link LatinIME#onCreate()}.
404         SubtypeLocaleUtils.init(pref.getContext());
405 
406         final Resources res = pref.getContext().getResources();
407         final SharedPreferences prefs = pref.getSharedPreferences();
408         final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
409         final InputMethodSubtype[] subtypes =
410                 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
411         final ArrayList<String> subtypeNames = new ArrayList<>();
412         for (final InputMethodSubtype subtype : subtypes) {
413             subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
414         }
415         // TODO: A delimiter of custom input styles should be localized.
416         pref.setSummary(TextUtils.join(", ", subtypeNames));
417     }
418 
419     @Override
onCreate(final Bundle savedInstanceState)420     public void onCreate(final Bundle savedInstanceState) {
421         super.onCreate(savedInstanceState);
422 
423         mPrefs = getPreferenceManager().getSharedPreferences();
424         RichInputMethodManager.init(getActivity());
425         mRichImm = RichInputMethodManager.getInstance();
426         addPreferencesFromResource(R.xml.additional_subtype_settings);
427         setHasOptionsMenu(true);
428     }
429 
430     @Override
onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState)431     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
432             final Bundle savedInstanceState) {
433         final View view = super.onCreateView(inflater, container, savedInstanceState);
434         // For correct display in RTL locales, we need to set the layout direction of the
435         // fragment's top view.
436         ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE);
437         return view;
438     }
439 
440     @Override
onActivityCreated(final Bundle savedInstanceState)441     public void onActivityCreated(final Bundle savedInstanceState) {
442         final Context context = getActivity();
443         mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context);
444         mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context);
445 
446         final String prefSubtypes =
447                 Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
448         setPrefSubtypes(prefSubtypes, context);
449 
450         mIsAddingNewSubtype = (savedInstanceState != null)
451                 && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE);
452         if (mIsAddingNewSubtype) {
453             getPreferenceScreen().addPreference(
454                     SubtypePreference.newIncompleteSubtypePreference(context, mSubtypeProxy));
455         }
456 
457         super.onActivityCreated(savedInstanceState);
458 
459         if (savedInstanceState != null && savedInstanceState.containsKey(
460                 KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) {
461             mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString(
462                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER);
463             final SubtypePreference subtypePref = (SubtypePreference)findPreference(
464                     mSubtypePreferenceKeyForSubtypeEnabler);
465             mSubtypeEnablerNotificationDialog = createDialog();
466             mSubtypeEnablerNotificationDialog.show();
467         }
468     }
469 
470     @Override
onSaveInstanceState(final Bundle outState)471     public void onSaveInstanceState(final Bundle outState) {
472         super.onSaveInstanceState(outState);
473         if (mIsAddingNewSubtype) {
474             outState.putBoolean(KEY_IS_ADDING_NEW_SUBTYPE, true);
475         }
476         if (mSubtypeEnablerNotificationDialog != null
477                 && mSubtypeEnablerNotificationDialog.isShowing()) {
478             outState.putBoolean(KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN, true);
479             outState.putString(
480                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER, mSubtypePreferenceKeyForSubtypeEnabler);
481         }
482     }
483 
484     private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() {
485         @Override
486         public void onRemovePressed(final SubtypePreference subtypePref) {
487             mIsAddingNewSubtype = false;
488             final PreferenceGroup group = getPreferenceScreen();
489             group.removePreference(subtypePref);
490             mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
491         }
492 
493         @Override
494         public void onSavePressed(final SubtypePreference subtypePref) {
495             final InputMethodSubtype subtype = subtypePref.getSubtype();
496             if (!subtypePref.hasBeenModified()) {
497                 return;
498             }
499             if (findDuplicatedSubtype(subtype) == null) {
500                 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
501                 return;
502             }
503 
504             // Saved subtype is duplicated.
505             final PreferenceGroup group = getPreferenceScreen();
506             group.removePreference(subtypePref);
507             subtypePref.revert();
508             group.addPreference(subtypePref);
509             showSubtypeAlreadyExistsToast(subtype);
510         }
511 
512         @Override
513         public void onAddPressed(final SubtypePreference subtypePref) {
514             mIsAddingNewSubtype = false;
515             final InputMethodSubtype subtype = subtypePref.getSubtype();
516             if (findDuplicatedSubtype(subtype) == null) {
517                 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
518                 mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey();
519                 mSubtypeEnablerNotificationDialog = createDialog();
520                 mSubtypeEnablerNotificationDialog.show();
521                 return;
522             }
523 
524             // Newly added subtype is duplicated.
525             final PreferenceGroup group = getPreferenceScreen();
526             group.removePreference(subtypePref);
527             showSubtypeAlreadyExistsToast(subtype);
528         }
529 
530         @Override
531         public SubtypeLocaleAdapter getSubtypeLocaleAdapter() {
532             return mSubtypeLocaleAdapter;
533         }
534 
535         @Override
536         public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() {
537             return mKeyboardLayoutSetAdapter;
538         }
539     };
540 
showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype)541     private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) {
542         final Context context = getActivity();
543         final Resources res = context.getResources();
544         final String message = res.getString(R.string.custom_input_style_already_exists,
545                 SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
546         Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
547     }
548 
findDuplicatedSubtype(final InputMethodSubtype subtype)549     private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
550         final String localeString = subtype.getLocale();
551         final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
552         return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
553                 localeString, keyboardLayoutSetName);
554     }
555 
createDialog()556     private AlertDialog createDialog() {
557         final AlertDialog.Builder builder = new AlertDialog.Builder(
558                 DialogUtils.getPlatformDialogThemeContext(getActivity()));
559         builder.setTitle(R.string.custom_input_styles_title)
560                 .setMessage(R.string.custom_input_style_note_message)
561                 .setNegativeButton(R.string.not_now, null)
562                 .setPositiveButton(R.string.enable, new DialogInterface.OnClickListener() {
563                     @Override
564                     public void onClick(DialogInterface dialog, int which) {
565                         final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
566                                 mRichImm.getInputMethodIdOfThisIme(),
567                                 Intent.FLAG_ACTIVITY_NEW_TASK
568                                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
569                                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
570                         // TODO: Add newly adding subtype to extra value of the intent as a hint
571                         // for the input language selection activity.
572                         // intent.putExtra("newlyAddedSubtype", subtypePref.getSubtype());
573                         startActivity(intent);
574                     }
575                 });
576 
577         return builder.create();
578     }
579 
setPrefSubtypes(final String prefSubtypes, final Context context)580     private void setPrefSubtypes(final String prefSubtypes, final Context context) {
581         final PreferenceGroup group = getPreferenceScreen();
582         group.removeAll();
583         final InputMethodSubtype[] subtypesArray =
584                 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes);
585         for (final InputMethodSubtype subtype : subtypesArray) {
586             final SubtypePreference pref = new SubtypePreference(
587                     context, subtype, mSubtypeProxy);
588             group.addPreference(pref);
589         }
590     }
591 
getSubtypes()592     private InputMethodSubtype[] getSubtypes() {
593         final PreferenceGroup group = getPreferenceScreen();
594         final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
595         final int count = group.getPreferenceCount();
596         for (int i = 0; i < count; i++) {
597             final Preference pref = group.getPreference(i);
598             if (pref instanceof SubtypePreference) {
599                 final SubtypePreference subtypePref = (SubtypePreference)pref;
600                 // We should not save newly adding subtype to preference because it is incomplete.
601                 if (subtypePref.isIncomplete()) continue;
602                 subtypes.add(subtypePref.getSubtype());
603             }
604         }
605         return subtypes.toArray(new InputMethodSubtype[subtypes.size()]);
606     }
607 
608     @Override
onPause()609     public void onPause() {
610         super.onPause();
611         final String oldSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
612         final InputMethodSubtype[] subtypes = getSubtypes();
613         final String prefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(subtypes);
614         if (prefSubtypes.equals(oldSubtypes)) {
615             return;
616         }
617         Settings.writePrefAdditionalSubtypes(mPrefs, prefSubtypes);
618         mRichImm.setAdditionalInputMethodSubtypes(subtypes);
619     }
620 
621     @Override
onCreateOptionsMenu(final Menu menu, final MenuInflater inflater)622     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
623         inflater.inflate(R.menu.add_style, menu);
624     }
625 
626     @Override
onOptionsItemSelected(final MenuItem item)627     public boolean onOptionsItemSelected(final MenuItem item) {
628         final int itemId = item.getItemId();
629         if (itemId == R.id.action_add_style) {
630             final SubtypePreference newSubtype =
631                     SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy);
632             getPreferenceScreen().addPreference(newSubtype);
633             newSubtype.show();
634             mIsAddingNewSubtype = true;
635             return true;
636         }
637         return super.onOptionsItemSelected(item);
638     }
639 }
640