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.content.Context;
     21 import android.content.DialogInterface;
     22 import android.content.Intent;
     23 import android.content.SharedPreferences;
     24 import android.content.res.Resources;
     25 import android.os.Bundle;
     26 import android.preference.Preference;
     27 import android.preference.PreferenceFragment;
     28 import android.preference.PreferenceGroup;
     29 import android.support.v4.view.ViewCompat;
     30 import android.text.TextUtils;
     31 import android.util.Log;
     32 import android.view.LayoutInflater;
     33 import android.view.Menu;
     34 import android.view.MenuInflater;
     35 import android.view.MenuItem;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.view.inputmethod.InputMethodSubtype;
     39 import android.widget.Toast;
     40 
     41 import com.android.inputmethod.latin.R;
     42 import com.android.inputmethod.latin.RichInputMethodManager;
     43 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
     44 import com.android.inputmethod.latin.utils.DialogUtils;
     45 import com.android.inputmethod.latin.utils.IntentUtils;
     46 import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
     47 
     48 import java.util.ArrayList;
     49 
     50 public final class CustomInputStyleSettingsFragment extends PreferenceFragment
     51         implements CustomInputStylePreference.Listener {
     52     private static final String TAG = CustomInputStyleSettingsFragment.class.getSimpleName();
     53     // Note: We would like to turn this debug flag true in order to see what input styles are
     54     // defined in a bug-report.
     55     private static final boolean DEBUG_CUSTOM_INPUT_STYLES = true;
     56 
     57     private RichInputMethodManager mRichImm;
     58     private SharedPreferences mPrefs;
     59     private CustomInputStylePreference.SubtypeLocaleAdapter mSubtypeLocaleAdapter;
     60     private CustomInputStylePreference.KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
     61 
     62     private boolean mIsAddingNewSubtype;
     63     private AlertDialog mSubtypeEnablerNotificationDialog;
     64     private String mSubtypePreferenceKeyForSubtypeEnabler;
     65 
     66     private static final String KEY_IS_ADDING_NEW_SUBTYPE = "is_adding_new_subtype";
     67     private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
     68             "is_subtype_enabler_notification_dialog_open";
     69     private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
     70 
     71     public CustomInputStyleSettingsFragment() {
     72         // Empty constructor for fragment generation.
     73     }
     74 
     75     static void updateCustomInputStylesSummary(final Preference pref) {
     76         // When we are called from the Settings application but we are not already running, some
     77         // singleton and utility classes may not have been initialized.  We have to call
     78         // initialization method of these classes here. See {@link LatinIME#onCreate()}.
     79         SubtypeLocaleUtils.init(pref.getContext());
     80 
     81         final Resources res = pref.getContext().getResources();
     82         final SharedPreferences prefs = pref.getSharedPreferences();
     83         final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
     84         final InputMethodSubtype[] subtypes =
     85                 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
     86         final ArrayList<String> subtypeNames = new ArrayList<>();
     87         for (final InputMethodSubtype subtype : subtypes) {
     88             subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
     89         }
     90         // TODO: A delimiter of custom input styles should be localized.
     91         pref.setSummary(TextUtils.join(", ", subtypeNames));
     92     }
     93 
     94     @Override
     95     public void onCreate(final Bundle savedInstanceState) {
     96         super.onCreate(savedInstanceState);
     97 
     98         mPrefs = getPreferenceManager().getSharedPreferences();
     99         RichInputMethodManager.init(getActivity());
    100         mRichImm = RichInputMethodManager.getInstance();
    101         addPreferencesFromResource(R.xml.additional_subtype_settings);
    102         setHasOptionsMenu(true);
    103     }
    104 
    105     @Override
    106     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
    107             final Bundle savedInstanceState) {
    108         final View view = super.onCreateView(inflater, container, savedInstanceState);
    109         // For correct display in RTL locales, we need to set the layout direction of the
    110         // fragment's top view.
    111         ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE);
    112         return view;
    113     }
    114 
    115     @Override
    116     public void onActivityCreated(final Bundle savedInstanceState) {
    117         final Context context = getActivity();
    118         mSubtypeLocaleAdapter = new CustomInputStylePreference.SubtypeLocaleAdapter(context);
    119         mKeyboardLayoutSetAdapter =
    120                 new CustomInputStylePreference.KeyboardLayoutSetAdapter(context);
    121 
    122         final String prefSubtypes =
    123                 Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
    124         if (DEBUG_CUSTOM_INPUT_STYLES) {
    125             Log.i(TAG, "Load custom input styles: " + prefSubtypes);
    126         }
    127         setPrefSubtypes(prefSubtypes, context);
    128 
    129         mIsAddingNewSubtype = (savedInstanceState != null)
    130                 && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE);
    131         if (mIsAddingNewSubtype) {
    132             getPreferenceScreen().addPreference(
    133                     CustomInputStylePreference.newIncompleteSubtypePreference(context, this));
    134         }
    135 
    136         super.onActivityCreated(savedInstanceState);
    137 
    138         if (savedInstanceState != null && savedInstanceState.containsKey(
    139                 KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) {
    140             mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString(
    141                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER);
    142             mSubtypeEnablerNotificationDialog = createDialog();
    143             mSubtypeEnablerNotificationDialog.show();
    144         }
    145     }
    146 
    147     @Override
    148     public void onSaveInstanceState(final Bundle outState) {
    149         super.onSaveInstanceState(outState);
    150         if (mIsAddingNewSubtype) {
    151             outState.putBoolean(KEY_IS_ADDING_NEW_SUBTYPE, true);
    152         }
    153         if (mSubtypeEnablerNotificationDialog != null
    154                 && mSubtypeEnablerNotificationDialog.isShowing()) {
    155             outState.putBoolean(KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN, true);
    156             outState.putString(
    157                     KEY_SUBTYPE_FOR_SUBTYPE_ENABLER, mSubtypePreferenceKeyForSubtypeEnabler);
    158         }
    159     }
    160 
    161     @Override
    162     public void onRemoveCustomInputStyle(final CustomInputStylePreference stylePref) {
    163         mIsAddingNewSubtype = false;
    164         final PreferenceGroup group = getPreferenceScreen();
    165         group.removePreference(stylePref);
    166         mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
    167     }
    168 
    169     @Override
    170     public void onSaveCustomInputStyle(final CustomInputStylePreference stylePref) {
    171         final InputMethodSubtype subtype = stylePref.getSubtype();
    172         if (!stylePref.hasBeenModified()) {
    173             return;
    174         }
    175         if (findDuplicatedSubtype(subtype) == null) {
    176             mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
    177             return;
    178         }
    179 
    180         // Saved subtype is duplicated.
    181         final PreferenceGroup group = getPreferenceScreen();
    182         group.removePreference(stylePref);
    183         stylePref.revert();
    184         group.addPreference(stylePref);
    185         showSubtypeAlreadyExistsToast(subtype);
    186     }
    187 
    188     @Override
    189     public void onAddCustomInputStyle(final CustomInputStylePreference stylePref) {
    190         mIsAddingNewSubtype = false;
    191         final InputMethodSubtype subtype = stylePref.getSubtype();
    192         if (findDuplicatedSubtype(subtype) == null) {
    193             mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
    194             mSubtypePreferenceKeyForSubtypeEnabler = stylePref.getKey();
    195             mSubtypeEnablerNotificationDialog = createDialog();
    196             mSubtypeEnablerNotificationDialog.show();
    197             return;
    198         }
    199 
    200         // Newly added subtype is duplicated.
    201         final PreferenceGroup group = getPreferenceScreen();
    202         group.removePreference(stylePref);
    203         showSubtypeAlreadyExistsToast(subtype);
    204     }
    205 
    206     @Override
    207     public CustomInputStylePreference.SubtypeLocaleAdapter getSubtypeLocaleAdapter() {
    208         return mSubtypeLocaleAdapter;
    209     }
    210 
    211     @Override
    212     public CustomInputStylePreference.KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() {
    213         return mKeyboardLayoutSetAdapter;
    214     }
    215 
    216     private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) {
    217         final Context context = getActivity();
    218         final Resources res = context.getResources();
    219         final String message = res.getString(R.string.custom_input_style_already_exists,
    220                 SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype));
    221         Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    222     }
    223 
    224     private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
    225         final String localeString = subtype.getLocale();
    226         final String keyboardLayoutSetName = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
    227         return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
    228                 localeString, keyboardLayoutSetName);
    229     }
    230 
    231     private AlertDialog createDialog() {
    232         final String imeId = mRichImm.getInputMethodIdOfThisIme();
    233         final AlertDialog.Builder builder = new AlertDialog.Builder(
    234                 DialogUtils.getPlatformDialogThemeContext(getActivity()));
    235         builder.setTitle(R.string.custom_input_styles_title)
    236                 .setMessage(R.string.custom_input_style_note_message)
    237                 .setNegativeButton(R.string.not_now, null)
    238                 .setPositiveButton(R.string.enable, new DialogInterface.OnClickListener() {
    239                     @Override
    240                     public void onClick(DialogInterface dialog, int which) {
    241                         final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
    242                                 imeId,
    243                                 Intent.FLAG_ACTIVITY_NEW_TASK
    244                                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    245                                 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    246                         // TODO: Add newly adding subtype to extra value of the intent as a hint
    247                         // for the input language selection activity.
    248                         // intent.putExtra("newlyAddedSubtype", subtypePref.getSubtype());
    249                         startActivity(intent);
    250                     }
    251                 });
    252 
    253         return builder.create();
    254     }
    255 
    256     private void setPrefSubtypes(final String prefSubtypes, final Context context) {
    257         final PreferenceGroup group = getPreferenceScreen();
    258         group.removeAll();
    259         final InputMethodSubtype[] subtypesArray =
    260                 AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtypes);
    261         for (final InputMethodSubtype subtype : subtypesArray) {
    262             final CustomInputStylePreference pref =
    263                     new CustomInputStylePreference(context, subtype, this);
    264             group.addPreference(pref);
    265         }
    266     }
    267 
    268     private InputMethodSubtype[] getSubtypes() {
    269         final PreferenceGroup group = getPreferenceScreen();
    270         final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
    271         final int count = group.getPreferenceCount();
    272         for (int i = 0; i < count; i++) {
    273             final Preference pref = group.getPreference(i);
    274             if (pref instanceof CustomInputStylePreference) {
    275                 final CustomInputStylePreference subtypePref = (CustomInputStylePreference)pref;
    276                 // We should not save newly adding subtype to preference because it is incomplete.
    277                 if (subtypePref.isIncomplete()) continue;
    278                 subtypes.add(subtypePref.getSubtype());
    279             }
    280         }
    281         return subtypes.toArray(new InputMethodSubtype[subtypes.size()]);
    282     }
    283 
    284     @Override
    285     public void onPause() {
    286         super.onPause();
    287         final String oldSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources());
    288         final InputMethodSubtype[] subtypes = getSubtypes();
    289         final String prefSubtypes = AdditionalSubtypeUtils.createPrefSubtypes(subtypes);
    290         if (DEBUG_CUSTOM_INPUT_STYLES) {
    291             Log.i(TAG, "Save custom input styles: " + prefSubtypes);
    292         }
    293         if (prefSubtypes.equals(oldSubtypes)) {
    294             return;
    295         }
    296         Settings.writePrefAdditionalSubtypes(mPrefs, prefSubtypes);
    297         mRichImm.setAdditionalInputMethodSubtypes(subtypes);
    298     }
    299 
    300     @Override
    301     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
    302         inflater.inflate(R.menu.add_style, menu);
    303     }
    304 
    305     @Override
    306     public boolean onOptionsItemSelected(final MenuItem item) {
    307         final int itemId = item.getItemId();
    308         if (itemId == R.id.action_add_style) {
    309             final CustomInputStylePreference newSubtype =
    310                     CustomInputStylePreference.newIncompleteSubtypePreference(getActivity(), this);
    311             getPreferenceScreen().addPreference(newSubtype);
    312             newSubtype.show();
    313             mIsAddingNewSubtype = true;
    314             return true;
    315         }
    316         return super.onOptionsItemSelected(item);
    317     }
    318 }
    319