Add AvailableVirtualKeyboardFragment to TV

Allows enabling/disabling IMEs on TV.
Moves InputMethodPreference to SettingsLib.
Also minor code tidying

Bug: 36079941
Test: Keyboard settings still work on Fugu and Ryu
Change-Id: Idf42cf5c46d5bb32db59924819d64f73d533d105
This commit is contained in:
Tony Mantler
2017-03-31 13:54:17 -07:00
parent 2dd96f613f
commit 2ec69563cd
4 changed files with 9 additions and 339 deletions

View File

@@ -3880,13 +3880,6 @@
<string name="show_password">Show passwords</string> <string name="show_password">Show passwords</string>
<!-- On Security & location settings screen. This is a short summary text describing what "Show passwords" setting does --> <!-- On Security & location settings screen. This is a short summary text describing what "Show passwords" setting does -->
<string name="show_password_summary">Display characters briefly as you type</string> <string name="show_password_summary">Display characters briefly as you type</string>
<!-- Warning message about security implications of enabling an input method, displayed as a dialog
message when the user selects to enable an IME. -->
<string name="ime_security_warning">This input method may be able to collect
all the text you type, including personal data like passwords and credit
card numbers. It comes from the app
<xliff:g id="ime_application_name">%1$s</xliff:g>.
Use this input method?</string>
<!-- Warning message about security implications of enabling a spell checker, displayed as a dialog <!-- Warning message about security implications of enabling a spell checker, displayed as a dialog
message when the user selects to enable a spell checker. --> message when the user selects to enable a spell checker. -->
<string name="spellchecker_security_warning">This spell checker may be able to collect <string name="spellchecker_security_warning">This spell checker may be able to collect
@@ -3898,9 +3891,6 @@
<string name="spellchecker_quick_settings">Settings</string> <string name="spellchecker_quick_settings">Settings</string>
<!-- Image button description for spell checker language. --> <!-- Image button description for spell checker language. -->
<string name="spellchecker_language">Language</string> <string name="spellchecker_language">Language</string>
<!-- Toast that settings for an application is failed to open. -->
<string name="failed_to_open_app_settings_toast">Failed to open settings for <xliff:g id="spell_application_name">%1$s</xliff:g></string>
<!-- Title for the 'keyboard and input methods' preference category. [CHAR LIMIT=45] --> <!-- Title for the 'keyboard and input methods' preference category. [CHAR LIMIT=45] -->
<string name="keyboard_and_input_methods_category">Keyboard &amp; inputs</string> <string name="keyboard_and_input_methods_category">Keyboard &amp; inputs</string>
<!-- Title for the 'virtual keyboard' preference sub-screen. [CHAR LIMIT=35] --> <!-- Title for the 'virtual keyboard' preference sub-screen. [CHAR LIMIT=35] -->

View File

@@ -43,12 +43,11 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable; import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw; import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodPreference;
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper; import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
import java.text.Collator; import java.text.Collator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFragment public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFragment
@@ -115,7 +114,7 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager, private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager,
@NonNull final InputMethodInfo imi) { @NonNull final InputMethodInfo imi) {
final ServiceInfo si = imi.getServiceInfo(); final ServiceInfo si = imi.getServiceInfo();
final ApplicationInfo ai = si.applicationInfo; final ApplicationInfo ai = si != null ? si.applicationInfo : null;
final String packageName = imi.getPackageName(); final String packageName = imi.getPackageName();
if (si == null || ai == null || packageName == null) { if (si == null || ai == null || packageName == null) {
return new ColorDrawable(Color.TRANSPARENT); return new ColorDrawable(Color.TRANSPARENT);
@@ -151,8 +150,8 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
final Context context = getPrefContext(); final Context context = getPrefContext();
final PackageManager packageManager = getActivity().getPackageManager(); final PackageManager packageManager = getActivity().getPackageManager();
final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList(); final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
final int N = (imis == null ? 0 : imis.size()); final int numImis = (imis == null ? 0 : imis.size());
for (int i = 0; i < N; ++i) { for (int i = 0; i < numImis; ++i) {
final InputMethodInfo imi = imis.get(i); final InputMethodInfo imi = imis.get(i);
final boolean isAllowedByOrganization = permittedList == null final boolean isAllowedByOrganization = permittedList == null
|| permittedList.contains(imi.getPackageName()); || permittedList.contains(imi.getPackageName());
@@ -162,14 +161,9 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
mInputMethodPreferenceList.add(pref); mInputMethodPreferenceList.add(pref);
} }
final Collator collator = Collator.getInstance(); final Collator collator = Collator.getInstance();
Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() { mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
@Override
public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
return lhs.compareTo(rhs, collator);
}
});
getPreferenceScreen().removeAll(); getPreferenceScreen().removeAll();
for (int i = 0; i < N; ++i) { for (int i = 0; i < numImis; ++i) {
final InputMethodPreference pref = mInputMethodPreferenceList.get(i); final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
pref.setOrder(i); pref.setOrder(i);
getPreferenceScreen().addPreference(pref); getPreferenceScreen().addPreference(pref);
@@ -190,8 +184,6 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context, static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context,
final List<InputMethodInfo> inputMethods, final String screenTitle) { final List<InputMethodInfo> inputMethods, final String screenTitle) {
final List<SearchIndexableRaw> indexes = new ArrayList<>(); final List<SearchIndexableRaw> indexes = new ArrayList<>();
final InputMethodManager imm = (InputMethodManager) context.getSystemService(
Context.INPUT_METHOD_SERVICE);
for (int i = 0; i < inputMethods.size(); i++) { for (int i = 0; i < inputMethods.size(); i++) {
final InputMethodInfo imi = inputMethods.get(i); final InputMethodInfo imi = inputMethods.get(i);
final ServiceInfo serviceInfo = imi.getServiceInfo(); final ServiceInfo serviceInfo = imi.getServiceInfo();
@@ -207,7 +199,7 @@ public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFr
return indexes; return indexes;
} }
public static Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() { new BaseSearchIndexProvider() {
@Override @Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {

View File

@@ -1,308 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.inputmethod;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;
import com.android.internal.inputmethod.InputMethodUtils;
import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
import java.text.Collator;
import java.util.List;
/**
* 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 RestrictedSwitchPreference implements OnPreferenceClickListener,
OnPreferenceChangeListener {
private static final String TAG = InputMethodPreference.class.getSimpleName();
private static final String EMPTY_TEXT = "";
private static final int NO_WIDGET = 0;
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 boolean mHasPriorityInSorting;
private final OnSavePreferenceListener mOnSaveListener;
private final InputMethodSettingValuesWrapper mInputMethodSettingValues;
private final boolean mIsAllowedByOrganization;
private AlertDialog mDialog = null;
/**
* 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 isAllowedByOrganization false if the IME has been disabled by a device or profile
* owner.
* @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 boolean isAllowedByOrganization,
final OnSavePreferenceListener onSaveListener) {
super(context);
setPersistent(false);
mImi = imi;
mIsAllowedByOrganization = isAllowedByOrganization;
mOnSaveListener = onSaveListener;
if (!isImeEnabler) {
// Remove switch widget.
setWidgetLayoutResource(NO_WIDGET);
}
// 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);
}
public InputMethodInfo getInputMethodInfo() {
return mImi;
}
private boolean isImeEnabler() {
// If this {@link SwitchPreference} doesn't have a widget layout, we explicitly hide the
// switch widget at constructor.
return getWidgetLayoutResource() != NO_WIDGET;
}
@Override
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.
setCheckedInternal(false);
return false;
}
if (InputMethodUtils.isSystemIme(mImi)) {
// Enable a system IME. No need to show a security warning dialog,
// but we might need to prompt if it's not Direct Boot aware.
if (mImi.getServiceInfo().directBootAware) {
setCheckedInternal(true);
} else {
showDirectBootWarnDialog();
}
} else {
// Once security is confirmed, we might prompt if the IME isn't
// Direct Boot aware.
showSecurityWarnDialog();
}
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());
// 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.
// This preference should also be disabled in case the admin does not allow this input
// method.
if (isAlwaysChecked && isImeEnabler()) {
setDisabledByAdmin(null);
setEnabled(false);
} else if (!mIsAllowedByOrganization) {
EnforcedAdmin admin =
RestrictedLockUtils.checkIfInputMethodDisallowed(getContext(),
mImi.getPackageName(), UserHandle.myUserId());
setDisabledByAdmin(admin);
} else {
setEnabled(true);
}
setChecked(mInputMethodSettingValues.isEnabledImi(mImi));
if (!isDisabledByAdmin()) {
setSummary(getSummaryString());
}
}
private InputMethodManager getInputMethodManager() {
return (InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
private String getSummaryString() {
final InputMethodManager imm = getInputMethodManager();
final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(mImi, true);
return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence(
subtypes, getContext(), mImi);
}
private void setCheckedInternal(boolean checked) {
super.setChecked(checked);
mOnSaveListener.onSaveInputMethodPreference(InputMethodPreference.this);
notifyChanged();
}
private void showSecurityWarnDialog() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
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 = mImi.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, but we might
// need to prompt if it's not Direct Boot aware.
if (mImi.getServiceInfo().directBootAware) {
setCheckedInternal(true);
} else {
showDirectBootWarnDialog();
}
}
});
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.
setCheckedInternal(false);
}
});
mDialog = builder.create();
mDialog.show();
}
private void showDirectBootWarnDialog() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
final Context context = getContext();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(true /* cancelable */);
builder.setMessage(context.getText(R.string.direct_boot_unaware_dialog_message));
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
setCheckedInternal(true);
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
setCheckedInternal(false);
}
});
mDialog = builder.create();
mDialog.show();
}
int compareTo(final InputMethodPreference rhs, final Collator collator) {
if (this == rhs) {
return 0;
}
if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) {
final CharSequence t0 = getTitle();
final CharSequence t1 = rhs.getTitle();
if (TextUtils.isEmpty(t0)) {
return 1;
}
if (TextUtils.isEmpty(t1)) {
return -1;
}
return collator.compare(t0.toString(), t1.toString());
}
// Prefer always checked system IMEs
return mHasPriorityInSorting ? -1 : 1;
}
}

View File

@@ -35,6 +35,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable; import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw; import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import com.android.settingslib.inputmethod.InputMethodPreference;
import java.text.Collator; import java.text.Collator;
import java.util.ArrayList; import java.util.ArrayList;
@@ -104,12 +105,7 @@ public final class VirtualKeyboardFragment extends SettingsPreferenceFragment im
mInputMethodPreferenceList.add(pref); mInputMethodPreferenceList.add(pref);
} }
final Collator collator = Collator.getInstance(); final Collator collator = Collator.getInstance();
Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() { mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
@Override
public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
return lhs.compareTo(rhs, collator);
}
});
getPreferenceScreen().removeAll(); getPreferenceScreen().removeAll();
for (int i = 0; i < N; ++i) { for (int i = 0; i < N; ++i) {
final InputMethodPreference pref = mInputMethodPreferenceList.get(i); final InputMethodPreference pref = mInputMethodPreferenceList.get(i);