Reorganize language & input settings
Bug: 14860252 Bug: 16115751 Change-Id: I198aabebc08421764b78e137e7f26d2a7772d452
This commit is contained in:
@@ -3116,8 +3116,10 @@
|
||||
<string name="input_methods_settings_title">Text input</string>
|
||||
<!-- Setting name for Input Method chooser -->
|
||||
<string name="input_method">Input method</string>
|
||||
<!-- Title for the option to press to choose the current input method [CHAR LIMIT=35] -->
|
||||
<string name="current_input_method">Default</string>
|
||||
<!-- Title for the option to press to enable or disable keyboards, also known as input methods [CHAR LIMIT=35] -->
|
||||
<string name="choose_input_methods">Choose Keyboards</string>
|
||||
<!-- Title for the option to press to choose the current keyboard, also known as input method [CHAR LIMIT=35] -->
|
||||
<string name="current_input_method">Current Keyboard</string>
|
||||
<!-- Title for setting the visibility of input method selector [CHAR LIMIT=35] -->
|
||||
<string name="input_method_selector">Input method selector</string>
|
||||
<!-- An option to always show input method selector automatically when needed [CHAR LIMIT=25] -->
|
||||
|
@@ -32,10 +32,17 @@
|
||||
android:key="key_user_dictionary_settings"
|
||||
android:title="@string/user_dict_settings_title" />
|
||||
|
||||
<PreferenceCategory android:key="keyboard_settings_category"
|
||||
<PreferenceCategory
|
||||
android:key="keyboard_settings_category"
|
||||
android:title="@string/keyboard_settings_category">
|
||||
<PreferenceScreen android:key="current_input_method"
|
||||
<!-- An intent for this preference will be populated programmatically. -->
|
||||
<PreferenceScreen
|
||||
android:key="choose_input_methods"
|
||||
android:title="@string/choose_input_methods" />
|
||||
<PreferenceScreen
|
||||
android:key="current_input_method"
|
||||
android:title="@string/current_input_method" />
|
||||
<!-- Enabled input method list will be populated programmatically here. -->
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
@@ -16,26 +16,15 @@
|
||||
|
||||
package com.android.settings.inputmethod;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Settings.KeyboardLayoutPickerActivity;
|
||||
import com.android.settings.Settings.SpellCheckersSettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.UserDictionarySettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.VoiceInputOutputSettings;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
@@ -47,7 +36,6 @@ import android.os.Handler;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceScreen;
|
||||
@@ -60,17 +48,33 @@ import android.view.InputDevice;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
import android.widget.BaseAdapter;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Settings.KeyboardLayoutPickerActivity;
|
||||
import com.android.settings.Settings.SpellCheckersSettingsActivity;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.SubSettings;
|
||||
import com.android.settings.UserDictionarySettings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.VoiceInputOutputSettings;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
|
||||
implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
|
||||
KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable {
|
||||
KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable,
|
||||
InputMethodPreference.onSavePreferenceListener {
|
||||
private static final String KEY_PHONE_LANGUAGE = "phone_language";
|
||||
private static final String KEY_CHOOSE_INPUT_METHODS = "choose_input_methods";
|
||||
private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
|
||||
private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
|
||||
private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
|
||||
@@ -95,37 +99,29 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
|
||||
private final ArrayList<PreferenceScreen> 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<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
|
||||
final Context context = getActivity();
|
||||
final List<InputMethodInfo> 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<InputMethodPreference>() {
|
||||
@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;
|
||||
|
@@ -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,
|
||||
// <code>pref</code> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
431
src/com/android/settings/inputmethod/InputMethodPreference.java
Normal file → Executable file
431
src/com/android/settings/inputmethod/InputMethodPreference.java
Normal file → Executable file
@@ -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<InputMethodSubtype> 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<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true);
|
||||
final ArrayList<CharSequence> 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;
|
||||
}
|
||||
}
|
||||
|
@@ -169,7 +169,7 @@ class InputMethodSettingValuesWrapper {
|
||||
return count;
|
||||
}
|
||||
|
||||
private boolean isEnabledImi(InputMethodInfo imi) {
|
||||
boolean isEnabledImi(InputMethodInfo imi) {
|
||||
final List<InputMethodInfo> enabledImis;
|
||||
synchronized (mMethodMap) {
|
||||
enabledImis = mSettings.getEnabledInputMethodListLocked();
|
||||
|
Reference in New Issue
Block a user