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