From 74e7c3e3601f4808854cf12bffe11ebf4c6ea8ab Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 9 Jul 2014 16:26:12 +0900 Subject: [PATCH] Reorganize language & input settings Bug: 14860252 Bug: 16115751 Change-Id: I198aabebc08421764b78e137e7f26d2a7772d452 --- res/values/strings.xml | 6 +- res/xml/language_settings.xml | 11 +- .../InputMethodAndLanguageSettings.java | 168 ++++--- .../InputMethodAndSubtypeUtil.java | 27 +- .../inputmethod/InputMethodPreference.java | 431 ++++++++---------- .../InputMethodSettingValuesWrapper.java | 2 +- 6 files changed, 292 insertions(+), 353 deletions(-) mode change 100644 => 100755 src/com/android/settings/inputmethod/InputMethodPreference.java diff --git a/res/values/strings.xml b/res/values/strings.xml index d8c3171056b..b52f40d6284 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3116,8 +3116,10 @@ Text input Input method - - Default + + Choose Keyboards + + Current Keyboard Input method selector diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml index c210312571e..d8e76033d53 100644 --- a/res/xml/language_settings.xml +++ b/res/xml/language_settings.xml @@ -32,10 +32,17 @@ android:key="key_user_dictionary_settings" android:title="@string/user_dict_settings_title" /> - - + + + mHardKeyboardPreferenceList = new ArrayList<>(); private InputManager mIm; private InputMethodManager mImm; - private boolean mIsOnlyImeSettings; + private boolean mShowsOnlyFullImeAndKeyboardList; private Handler mHandler; private SettingsObserver mSettingsObserver; private Intent mIntentWaitingForResult; private InputMethodSettingValuesWrapper mInputMethodSettingValues; - private final OnPreferenceChangeListener mOnImePreferenceChangedListener = - new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference arg0, Object arg1) { - InputMethodSettingValuesWrapper.getInstance( - arg0.getContext()).refreshAllInputMethodAndSubtypes(); - ((BaseAdapter)getPreferenceScreen().getRootAdapter()).notifyDataSetChanged(); - updateInputMethodPreferenceViews(); - return true; - } - }; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.language_settings); + final Activity activity = getActivity(); + mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity); + try { mDefaultInputMethodSelectorVisibility = Integer.valueOf( getString(R.string.input_method_selector_visibility_default_value)); } catch (NumberFormatException e) { } - if (getActivity().getAssets().getLocales().length == 1) { + if (activity.getAssets().getLocales().length == 1) { // No "Select language" pref if there's only one system locale available. getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE)); } else { @@ -149,45 +145,42 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment "game_controller_settings_category"); // Filter out irrelevant features if invoked from IME settings button. - mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( - getActivity().getIntent().getAction()); - getActivity().getIntent().setAction(null); - if (mIsOnlyImeSettings) { + mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( + activity.getIntent().getAction()); + activity.getIntent().setAction(null); + if (mShowsOnlyFullImeAndKeyboardList) { getPreferenceScreen().removeAll(); getPreferenceScreen().addPreference(mHardKeyboardCategory); if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); } + mKeyboardSettingsCategory.removeAll(); getPreferenceScreen().addPreference(mKeyboardSettingsCategory); - } - - // Build IME preference category. - mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(getActivity()); - - mKeyboardSettingsCategory.removeAll(); - if (!mIsOnlyImeSettings) { - final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null); - currentIme.setKey(KEY_CURRENT_INPUT_METHOD); - currentIme.setTitle(getResources().getString(R.string.current_input_method)); - mKeyboardSettingsCategory.addPreference(currentIme); + } else { + final Preference pref = findPreference(KEY_CHOOSE_INPUT_METHODS); + final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS); + intent.setClass(activity, SubSettings.class); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, getClass().getName()); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, + R.string.choose_input_methods); + pref.setIntent(intent); } // Build hard keyboard and game controller preference categories. - mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE); + mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE); updateInputDevices(); // Spell Checker - final Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClass(getActivity(), SpellCheckersSettingsActivity.class); final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference( "spellcheckers_settings")); if (scp != null) { + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(activity, SpellCheckersSettingsActivity.class); scp.setFragmentIntent(this, intent); } mHandler = new Handler(); - mSettingsObserver = new SettingsObserver(mHandler, getActivity()); + mSettingsObserver = new SettingsObserver(mHandler, activity); } private void updateInputMethodSelectorSummary(int value) { @@ -246,7 +239,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment mSettingsObserver.resume(); mIm.registerInputDeviceListener(this, null); - if (!mIsOnlyImeSettings) { + if (!mShowsOnlyFullImeAndKeyboardList) { if (mLanguagePref != null) { String localeName = getLocaleName(getResources()); mLanguagePref.setSummary(localeName); @@ -419,31 +412,33 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment private void updateInputMethodPreferenceViews() { synchronized (mInputMethodPreferenceList) { // Clear existing "InputMethodPreference"s - for (final InputMethodPreference imp : mInputMethodPreferenceList) { - mKeyboardSettingsCategory.removePreference(imp); + for (final InputMethodPreference pref : mInputMethodPreferenceList) { + mKeyboardSettingsCategory.removePreference(pref); } mInputMethodPreferenceList.clear(); - final List imis = mInputMethodSettingValues.getInputMethodList(); + final Context context = getActivity(); + final List imis = mShowsOnlyFullImeAndKeyboardList + ? mInputMethodSettingValues.getInputMethodList() + : mImm.getEnabledInputMethodList(); final int N = (imis == null ? 0 : imis.size()); for (int i = 0; i < N; ++i) { final InputMethodInfo imi = imis.get(i); - final InputMethodPreference pref = getInputMethodPreference(imi); - pref.setOnImePreferenceChangeListener(mOnImePreferenceChangedListener); + final InputMethodPreference pref = new InputMethodPreference( + context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */, this); mInputMethodPreferenceList.add(pref); } - - if (!mInputMethodPreferenceList.isEmpty()) { - Collections.sort(mInputMethodPreferenceList); - for (int i = 0; i < N; ++i) { - mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i)); - } - } - - // update views status - for (Preference pref : mInputMethodPreferenceList) { - if (pref instanceof InputMethodPreference) { - ((InputMethodPreference) pref).updatePreferenceViews(); + final Collator collator = Collator.getInstance(); + Collections.sort(mInputMethodPreferenceList, new Comparator() { + @Override + public int compare(InputMethodPreference lhs, InputMethodPreference rhs) { + return lhs.compareTo(rhs, collator); } + }); + for (int i = 0; i < N; ++i) { + final InputMethodPreference pref = mInputMethodPreferenceList.get(i); + mKeyboardSettingsCategory.addPreference(pref); + InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); + pref.updatePreferenceViews(); } } updateCurrentImeName(); @@ -456,6 +451,19 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment mInputMethodSettingValues.getInputMethodList(), null); } + @Override + public void onSaveInputMethodPreference(final InputMethodPreference pref) { + final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard + == Configuration.KEYBOARD_QWERTY; + InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), + mImm.getInputMethodList(), hasHardwareKeyboard); + // Update input method settings and preference list. + mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); + for (final InputMethodPreference p : mInputMethodPreferenceList) { + p.updatePreferenceViews(); + } + } + private void updateCurrentImeName() { final Context context = getActivity(); if (context == null || mImm == null) return; @@ -471,26 +479,6 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment } } - private InputMethodPreference getInputMethodPreference(InputMethodInfo imi) { - final PackageManager pm = getPackageManager(); - final CharSequence label = imi.loadLabel(pm); - // IME settings - final Intent intent; - final String settingsActivity = imi.getSettingsActivity(); - if (!TextUtils.isEmpty(settingsActivity)) { - intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(imi.getPackageName(), settingsActivity); - } else { - intent = null; - } - - // Add a check box for enabling/disabling IME - final InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi); - pref.setKey(imi.getId()); - pref.setTitle(label); - return pref; - } - private void updateInputDevices() { updateHardKeyboards(); updateGameControllers(); @@ -643,7 +631,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment if (context.getAssets().getLocales().length > 1) { String localeName = getLocaleName(resources); SearchIndexableRaw indexable = new SearchIndexableRaw(context); - indexable.key = "phone_language"; + indexable.key = KEY_PHONE_LANGUAGE; indexable.title = context.getString(R.string.phone_language); indexable.summaryOn = localeName; indexable.summaryOff = localeName; @@ -681,7 +669,7 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment // Current IME. String currImeName = immValues.getCurrentInputMethodName(context).toString(); indexable = new SearchIndexableRaw(context); - indexable.key = "current_input_method"; + indexable.key = KEY_CURRENT_INPUT_METHOD; indexable.title = context.getString(R.string.current_input_method); indexable.summaryOn = currImeName; indexable.summaryOff = currImeName; diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java index bf3a6016951..f4e66d24bb4 100644 --- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java +++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java @@ -16,13 +16,12 @@ package com.android.settings.inputmethod; -import com.android.internal.inputmethod.InputMethodUtils; -import com.android.settings.SettingsPreferenceFragment; - import android.content.ContentResolver; +import android.content.SharedPreferences; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceScreen; +import android.preference.TwoStatePreference; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -30,6 +29,9 @@ import android.util.Log; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.inputmethod.InputMethodUtils; +import com.android.settings.SettingsPreferenceFragment; + import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -163,10 +165,10 @@ class InputMethodAndSubtypeUtil { final String imiId = imi.getId(); Preference pref = context.findPreference(imiId); if (pref == null) continue; - // In the Configure input method screen or in the subtype enabler screen. - // pref is instance of CheckBoxPreference in the Configure input method screen. - final boolean isImeChecked = (pref instanceof CheckBoxPreference) ? - ((CheckBoxPreference) pref).isChecked() + // In the choose input method screen or in the subtype enabler screen, + // pref is an instance of TwoStatePreference. + final boolean isImeChecked = (pref instanceof TwoStatePreference) ? + ((TwoStatePreference) pref).isChecked() : enabledIMEAndSubtypesMap.containsKey(imiId); final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId); final boolean systemIme = InputMethodUtils.isSystemIme(imi); @@ -338,4 +340,15 @@ class InputMethodAndSubtypeUtil { } } } + + static void removeUnnecessaryNonPersistentPreference(final Preference pref) { + final String key = pref.getKey(); + if (pref.isPersistent() || key == null) { + return; + } + final SharedPreferences prefs = pref.getSharedPreferences(); + if (prefs != null && prefs.contains(key)) { + prefs.edit().remove(key).apply(); + } + } } diff --git a/src/com/android/settings/inputmethod/InputMethodPreference.java b/src/com/android/settings/inputmethod/InputMethodPreference.java old mode 100644 new mode 100755 index 87a0a77b5d3..140487571b5 --- a/src/com/android/settings/inputmethod/InputMethodPreference.java +++ b/src/com/android/settings/inputmethod/InputMethodPreference.java @@ -16,304 +16,233 @@ package com.android.settings.inputmethod; -import com.android.internal.inputmethod.InputMethodUtils; -import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.Utils; - import android.app.AlertDialog; -import android.app.Fragment; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; -import android.preference.CheckBoxPreference; import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.SwitchPreference; import android.text.TextUtils; import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnLongClickListener; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; +import com.android.internal.inputmethod.InputMethodUtils; +import com.android.settings.R; + import java.text.Collator; +import java.util.ArrayList; import java.util.List; -// TODO: Make this non-persistent. -class InputMethodPreference extends CheckBoxPreference { +/** + * Input method preference. + * + * This preference represents an IME. It is used for two purposes. 1) An instance with a switch + * is used to enable or disable the IME. 2) An instance without a switch is used to invoke the + * setting activity of the IME. + */ +class InputMethodPreference extends SwitchPreference implements OnPreferenceClickListener, + OnPreferenceChangeListener { private static final String TAG = InputMethodPreference.class.getSimpleName(); - private final SettingsPreferenceFragment mFragment; + private static final String EMPTY_TEXT = ""; + + interface onSavePreferenceListener { + /** + * Called when this preference needs to be saved its state. + * + * Note that this preference is non-persistent and needs explicitly to be saved its state. + * Because changing one IME state may change other IMEs' state, this is a place to update + * other IMEs' state as well. + * + * @param pref This preference. + */ + public void onSaveInputMethodPreference(InputMethodPreference pref); + } + private final InputMethodInfo mImi; - private final InputMethodManager mImm; - private final boolean mIsValidSystemNonAuxAsciiCapableIme; - private final Intent mSettingsIntent; - private final boolean mIsSystemIme; - private final Collator mCollator; + private final boolean mHasPriorityInSorting; + private final onSavePreferenceListener mOnSaveListener; + private final InputMethodSettingValuesWrapper mInputMethodSettingValues; private AlertDialog mDialog = null; - private ImageView mInputMethodSettingsButton; - private TextView mTitleText; - private TextView mSummaryText; - private View mInputMethodPref; - private OnPreferenceChangeListener mOnImePreferenceChangeListener; - private final OnClickListener mPrefOnclickListener = new OnClickListener() { - @Override - public void onClick(View arg0) { - if (!isEnabled()) { - return; - } - if (isChecked()) { - setChecked(false, true /* save */); - } else { - if (mIsSystemIme) { - setChecked(true, true /* save */); - } else { - showSecurityWarnDialog(mImi, InputMethodPreference.this); - } - } - } - }; - - public InputMethodPreference(SettingsPreferenceFragment fragment, Intent settingsIntent, - InputMethodManager imm, InputMethodInfo imi) { - super(fragment.getActivity()); - setLayoutResource(R.layout.preference_inputmethod); - setWidgetLayoutResource(R.layout.preference_inputmethod_widget); - mFragment = fragment; - mSettingsIntent = settingsIntent; - mImm = imm; + /** + * A preference entry of an input method. + * + * @param context The Context this is associated with. + * @param imi The {@link InputMethodInfo} of this preference. + * @param isImeEnabler true if this preference is the IME enabler that has enable/disable + * switches for all available IMEs, not the list of enabled IMEs. + * @param onSaveListener The listener called when this preference has been changed and needs + * to save the state to shared preference. + */ + InputMethodPreference(final Context context, final InputMethodInfo imi, + final boolean isImeEnabler, final onSavePreferenceListener onSaveListener) { + super(context); + setPersistent(false); mImi = imi; - mIsSystemIme = InputMethodUtils.isSystemIme(imi); - mCollator = Collator.getInstance(fragment.getResources().getConfiguration().locale); - final Context context = fragment.getActivity(); - mIsValidSystemNonAuxAsciiCapableIme = InputMethodSettingValuesWrapper - .getInstance(context).isValidSystemNonAuxAsciiCapableIme(imi, context); - updatePreferenceViews(); + mOnSaveListener = onSaveListener; + if (!isImeEnabler) { + // Hide switch widget. + setWidgetLayoutResource(0 /* widgetLayoutResId */); + } + // Disable on/off switch texts. + setSwitchTextOn(EMPTY_TEXT); + setSwitchTextOff(EMPTY_TEXT); + setKey(imi.getId()); + setTitle(imi.loadLabel(context.getPackageManager())); + final String settingsActivity = imi.getSettingsActivity(); + if (TextUtils.isEmpty(settingsActivity)) { + setIntent(null); + } else { + // Set an intent to invoke settings activity of an input method. + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(imi.getPackageName(), settingsActivity); + setIntent(intent); + } + mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context); + mHasPriorityInSorting = InputMethodUtils.isSystemIme(imi) + && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context); + setOnPreferenceClickListener(this); + setOnPreferenceChangeListener(this); + } + + private boolean isImeEnabler() { + // If this {@link SwitchPreference} doesn't have a widget layout, we explicitly hide the + // switch widget at constructor. + return getWidgetLayoutResource() != 0; } @Override - protected void onBindView(View view) { - super.onBindView(view); - mInputMethodPref = view.findViewById(R.id.inputmethod_pref); - mInputMethodPref.setOnClickListener(mPrefOnclickListener); - mInputMethodSettingsButton = (ImageView)view.findViewById(R.id.inputmethod_settings); - mTitleText = (TextView)view.findViewById(android.R.id.title); - mSummaryText = (TextView)view.findViewById(android.R.id.summary); - final boolean hasSubtypes = mImi.getSubtypeCount() > 1; - final String imiId = mImi.getId(); - if (hasSubtypes) { - mInputMethodPref.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View arg0) { - final Bundle bundle = new Bundle(); - bundle.putString(android.provider.Settings.EXTRA_INPUT_METHOD_ID, imiId); - startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), - 0, bundle); - return true; - } - }); - } - - if (mSettingsIntent != null) { - mInputMethodSettingsButton.setOnClickListener( - new OnClickListener() { - @Override - public void onClick(View arg0) { - try { - mFragment.startActivity(mSettingsIntent); - } catch (ActivityNotFoundException e) { - Log.d(TAG, "IME's Settings Activity Not Found: " + e); - final String msg = mFragment.getString( - R.string.failed_to_open_app_settings_toast, - mImi.loadLabel( - mFragment.getActivity().getPackageManager())); - Toast.makeText( - mFragment.getActivity(), msg, Toast.LENGTH_LONG).show(); - } - } - }); - } - if (hasSubtypes) { - final OnLongClickListener listener = new OnLongClickListener() { - @Override - public boolean onLongClick(View arg0) { - final Bundle bundle = new Bundle(); - bundle.putString(android.provider.Settings.EXTRA_INPUT_METHOD_ID, imiId); - startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), - 0, bundle); - return true; - } - }; - mInputMethodSettingsButton.setOnLongClickListener(listener); - } - if (mSettingsIntent == null) { - mInputMethodSettingsButton.setVisibility(View.GONE); - } - updatePreferenceViews(); - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - updatePreferenceViews(); - } - - public void updatePreferenceViews() { - final boolean isAlwaysChecked = - InputMethodSettingValuesWrapper.getInstance(getContext()).isAlwaysCheckedIme( - mImi, getContext()); - if (isAlwaysChecked) { - super.setChecked(true); - super.setEnabled(false); - } else { - super.setEnabled(true); - } - final boolean checked = isChecked(); - if (mInputMethodSettingsButton != null) { - mInputMethodSettingsButton.setEnabled(checked); - mInputMethodSettingsButton.setClickable(checked); - mInputMethodSettingsButton.setFocusable(checked); - if (!checked) { - mInputMethodSettingsButton.setAlpha(Utils.DISABLED_ALPHA); - } - } - if (mTitleText != null) { - mTitleText.setEnabled(true); - } - if (mSummaryText != null) { - mSummaryText.setEnabled(checked); - } - if (mInputMethodPref != null) { - mInputMethodPref.setEnabled(true); - mInputMethodPref.setLongClickable(checked); - final boolean enabled = isEnabled(); - mInputMethodPref.setOnClickListener(enabled ? mPrefOnclickListener : null); - if (!enabled) { - mInputMethodPref.setBackgroundColor(0); - } - } - updateSummary(); - } - - public static boolean startFragment( - Fragment fragment, String fragmentClass, int requestCode, Bundle extras) { - if (fragment.getActivity() instanceof SettingsActivity) { - SettingsActivity sa = (SettingsActivity) fragment.getActivity(); - sa.startPreferencePanel(fragmentClass, extras, 0, null, fragment, requestCode); - return true; - } else { - Log.w(TAG, "Parent isn't Settings, thus there's no way to launch the " - + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode - + ")"); + public boolean onPreferenceChange(final Preference preference, final Object newValue) { + // Always returns false to prevent default behavior. + // See {@link TwoStatePreference#onClick()}. + if (!isImeEnabler()) { + // Prevent disabling an IME because this preference is for invoking a settings activity. return false; } + if (isChecked()) { + // Disable this IME. + setChecked(false); + mOnSaveListener.onSaveInputMethodPreference(this); + return false; + } + if (InputMethodUtils.isSystemIme(mImi)) { + // Enable a system IME. No need to show a security warning dialog. + setChecked(true); + mOnSaveListener.onSaveInputMethodPreference(this); + return false; + } + // Enable a 3rd party IME. + showSecurityWarnDialog(mImi); + return false; + } + + @Override + public boolean onPreferenceClick(final Preference preference) { + // Always returns true to prevent invoking an intent without catching exceptions. + // See {@link Preference#performClick(PreferenceScreen)}/ + if (isImeEnabler()) { + // Prevent invoking a settings activity because this preference is for enabling and + // disabling an input method. + return true; + } + final Context context = getContext(); + try { + final Intent intent = getIntent(); + if (intent != null) { + // Invoke a settings activity of an input method. + context.startActivity(intent); + } + } catch (final ActivityNotFoundException e) { + Log.d(TAG, "IME's Settings Activity Not Found", e); + final String message = context.getString( + R.string.failed_to_open_app_settings_toast, + mImi.loadLabel(context.getPackageManager())); + Toast.makeText(context, message, Toast.LENGTH_LONG).show(); + } + return true; + } + + void updatePreferenceViews() { + final boolean isAlwaysChecked = mInputMethodSettingValues.isAlwaysCheckedIme( + mImi, getContext()); + // Only when this preference has a switch and an input method should be always enabled, + // this preference should be disabled to prevent accidentally disabling an input method. + setEnabled(!(isAlwaysChecked && isImeEnabler())); + setChecked(mInputMethodSettingValues.isEnabledImi(mImi)); + setSummary(getSummaryString()); + } + + private InputMethodManager getInputMethodManager() { + return (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE); } private String getSummaryString() { - final StringBuilder builder = new StringBuilder(); - final List subtypes = mImm.getEnabledInputMethodSubtypeList(mImi, true); - for (InputMethodSubtype subtype : subtypes) { - if (builder.length() > 0) { - builder.append(", "); - } - final CharSequence subtypeLabel = subtype.getDisplayName(mFragment.getActivity(), - mImi.getPackageName(), mImi.getServiceInfo().applicationInfo); - builder.append(subtypeLabel); + final Context context = getContext(); + final InputMethodManager imm = getInputMethodManager(); + final List subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true); + final ArrayList subtypeLabels = new ArrayList<>(); + for (final InputMethodSubtype subtype : subtypes) { + final CharSequence label = subtype.getDisplayName( + context, mImi.getPackageName(), mImi.getServiceInfo().applicationInfo); + subtypeLabels.add(label); } - return builder.toString(); + // TODO: A delimiter of subtype labels should be localized. + return TextUtils.join(", ", subtypeLabels); } - private void updateSummary() { - final String summary = getSummaryString(); - if (TextUtils.isEmpty(summary)) { - return; - } - setSummary(summary); - } - - /** - * Sets the checkbox state and optionally saves the settings. - * @param checked whether to check the box - * @param save whether to save IME settings - */ - private void setChecked(boolean checked, boolean save) { - final boolean wasChecked = isChecked(); - super.setChecked(checked); - if (save) { - saveImeSettings(); - if (wasChecked != checked && mOnImePreferenceChangeListener != null) { - mOnImePreferenceChangeListener.onPreferenceChange(this, checked); - } - } - } - - public void setOnImePreferenceChangeListener(OnPreferenceChangeListener listener) { - mOnImePreferenceChangeListener = listener; - } - - private void showSecurityWarnDialog(InputMethodInfo imi, final InputMethodPreference chkPref) { + private void showSecurityWarnDialog(final InputMethodInfo imi) { if (mDialog != null && mDialog.isShowing()) { mDialog.dismiss(); } - mDialog = (new AlertDialog.Builder(mFragment.getActivity())) - .setTitle(android.R.string.dialog_alert_title) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - chkPref.setChecked(true, true); - } - }) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - }) - .create(); - mDialog.setMessage(mFragment.getResources().getString(R.string.ime_security_warning, - imi.getServiceInfo().applicationInfo.loadLabel( - mFragment.getActivity().getPackageManager()))); + final Context context = getContext(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setCancelable(true /* cancelable */); + builder.setTitle(android.R.string.dialog_alert_title); + final CharSequence label = imi.getServiceInfo().applicationInfo.loadLabel( + context.getPackageManager()); + builder.setMessage(context.getString(R.string.ime_security_warning, label)); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + // The user confirmed to enable a 3rd party IME. + setChecked(true); + mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); + notifyChanged(); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + // The user canceled to enable a 3rd party IME. + setChecked(false); + mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this); + notifyChanged(); + } + }); + mDialog = builder.create(); mDialog.show(); } - @Override - public int compareTo(Preference p) { - if (!(p instanceof InputMethodPreference)) { - return super.compareTo(p); - } - final InputMethodPreference imp = (InputMethodPreference) p; - final boolean priority0 = mIsSystemIme && mIsValidSystemNonAuxAsciiCapableIme; - final boolean priority1 = imp.mIsSystemIme && imp.mIsValidSystemNonAuxAsciiCapableIme; - if (priority0 == priority1) { + int compareTo(final InputMethodPreference rhs, final Collator collator) { + if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) { final CharSequence t0 = getTitle(); - final CharSequence t1 = imp.getTitle(); + final CharSequence t1 = rhs.getTitle(); if (TextUtils.isEmpty(t0)) { return 1; } if (TextUtils.isEmpty(t1)) { return -1; } - return mCollator.compare(t0.toString(), t1.toString()); + return collator.compare(t0.toString(), t1.toString()); } // Prefer always checked system IMEs - return priority0 ? -1 : 1; - } - - private void saveImeSettings() { - InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( - mFragment, mFragment.getActivity().getContentResolver(), mImm.getInputMethodList(), - mFragment.getResources().getConfiguration().keyboard - == Configuration.KEYBOARD_QWERTY); + return mHasPriorityInSorting ? -1 : 1; } } diff --git a/src/com/android/settings/inputmethod/InputMethodSettingValuesWrapper.java b/src/com/android/settings/inputmethod/InputMethodSettingValuesWrapper.java index bf63487f053..ada476e15c2 100644 --- a/src/com/android/settings/inputmethod/InputMethodSettingValuesWrapper.java +++ b/src/com/android/settings/inputmethod/InputMethodSettingValuesWrapper.java @@ -169,7 +169,7 @@ class InputMethodSettingValuesWrapper { return count; } - private boolean isEnabledImi(InputMethodInfo imi) { + boolean isEnabledImi(InputMethodInfo imi) { final List enabledImis; synchronized (mMethodMap) { enabledImis = mSettings.getEnabledInputMethodListLocked();