Home | History | Annotate | Download | only in settings
      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> {
     78         public SubtypeLocaleItem(final String localeString, final String displayName) {
     79             super(localeString, displayName);
     80         }
     81 
     82         public SubtypeLocaleItem(final String localeString) {
     83             this(localeString,
     84                     SubtypeLocaleUtils.getSubtypeLocaleDisplayNameInSystemLocale(localeString));
     85         }
     86 
     87         @Override
     88         public String toString() {
     89             return second;
     90         }
     91 
     92         @Override
     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 
    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 
    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> {
    136         public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
    137             super(SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype),
    138                     SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype));
    139         }
    140 
    141         @Override
    142         public String toString() {
    143             return second;
    144         }
    145     }
    146 
    147     static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
    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 {
    164         public void onRemovePressed(SubtypePreference subtypePref);
    165         public void onSavePressed(SubtypePreference subtypePref);
    166         public void onAddPressed(SubtypePreference subtypePref);
    167         public SubtypeLocaleAdapter getSubtypeLocaleAdapter();
    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 
    183         public static SubtypePreference newIncompleteSubtypePreference(final Context context,
    184                 final SubtypeDialogProxy proxy) {
    185             return new SubtypePreference(context, null, proxy);
    186         }
    187 
    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 
    197         public void show() {
    198             showDialog(null);
    199         }
    200 
    201         public final boolean isIncomplete() {
    202             return mSubtype == null;
    203         }
    204 
    205         public InputMethodSubtype getSubtype() {
    206             return mSubtype;
    207         }
    208 
    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 
    226         public void revert() {
    227             setSubtype(mPreviousSubtype);
    228         }
    229 
    230         public boolean hasBeenModified() {
    231             return mSubtype != null && !mSubtype.equals(mPreviousSubtype);
    232         }
    233 
    234         @Override
    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
    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 
    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
    282         public void onCancel(final DialogInterface dialog) {
    283             if (isIncomplete()) {
    284                 mProxy.onRemovePressed(this);
    285             }
    286         }
    287 
    288         @Override
    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 
    318         private static int getSpinnerPosition(final Spinner spinner) {
    319             if (spinner == null) return -1;
    320             return spinner.getSelectedItemPosition();
    321         }
    322 
    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
    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
    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 
    362             public SavedState(final Parcelable superState) {
    363                 super(superState);
    364             }
    365 
    366             @Override
    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 
    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 
    396     public CustomInputStyleSettingsFragment() {
    397         // Empty constructor for fragment generation.
    398     }
    399 
    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
    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
    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
    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
    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 
    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 
    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 
    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 
    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 
    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
    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
    622     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
    623         inflater.inflate(R.menu.add_style, menu);
    624     }
    625 
    626     @Override
    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