blob: 13f44358ccf01b2301374e6244734e8370499c78 [file] [log] [blame]
/*
* Copyright (C) 2010 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 com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment {
private static final String TAG =InputMethodAndSubtypeEnabler.class.getSimpleName();
private AlertDialog mDialog = null;
private boolean mHaveHardKeyboard;
final private HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap =
new HashMap<String, List<Preference>>();
final private HashMap<String, CheckBoxPreference> mSubtypeAutoSelectionCBMap =
new HashMap<String, CheckBoxPreference>();
private InputMethodManager mImm;
private List<InputMethodInfo> mInputMethodProperties;
private String mInputMethodId;
private String mTitle;
private String mSystemLocale = "";
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
final Configuration config = getResources().getConfiguration();
mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
final Bundle arguments = getArguments();
// Input method id should be available from an Intent when this preference is launched as a
// single Activity (see InputMethodAndSubtypeEnablerActivity). It should be available
// from a preference argument when the preference is launched as a part of the other
// Activity (like a right pane of 2-pane Settings app)
mInputMethodId = getActivity().getIntent().getStringExtra(
android.provider.Settings.EXTRA_INPUT_METHOD_ID);
if (mInputMethodId == null && (arguments != null)) {
final String inputMethodId =
arguments.getString(android.provider.Settings.EXTRA_INPUT_METHOD_ID);
if (inputMethodId != null) {
mInputMethodId = inputMethodId;
}
}
mTitle = getActivity().getIntent().getStringExtra(Intent.EXTRA_TITLE);
if (mTitle == null && (arguments != null)) {
final String title = arguments.getString(Intent.EXTRA_TITLE);
if (title != null) {
mTitle = title;
}
}
mSystemLocale = config.locale.toString();
onCreateIMM();
setPreferenceScreen(createPreferenceHierarchy());
}
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
if (!TextUtils.isEmpty(mTitle)) {
getActivity().setTitle(mTitle);
}
}
@Override
public void onResume() {
super.onResume();
InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
this, getContentResolver(), mInputMethodProperties, mInputMethodAndSubtypePrefsMap);
updateAutoSelectionCB();
}
@Override
public void onPause() {
super.onPause();
// Clear all subtypes of all IMEs to make sure
clearImplicitlyEnabledSubtypes(null);
InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
mInputMethodProperties, mHaveHardKeyboard);
}
@Override
public boolean onPreferenceTreeClick(
PreferenceScreen preferenceScreen, Preference preference) {
if (preference instanceof CheckBoxPreference) {
final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
for (String imiId: mSubtypeAutoSelectionCBMap.keySet()) {
if (mSubtypeAutoSelectionCBMap.get(imiId) == chkPref) {
// We look for the first preference item in subtype enabler.
// The first item is used for turning on/off subtype auto selection.
// We are in the subtype enabler and trying selecting subtypes automatically.
setSubtypeAutoSelectionEnabled(imiId, chkPref.isChecked());
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
}
final String id = chkPref.getKey();
if (chkPref.isChecked()) {
InputMethodInfo selImi = null;
final int N = mInputMethodProperties.size();
for (int i = 0; i < N; i++) {
InputMethodInfo imi = mInputMethodProperties.get(i);
if (id.equals(imi.getId())) {
selImi = imi;
if (InputMethodAndSubtypeUtil.isSystemIme(imi)) {
InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
this, mInputMethodProperties, id, true);
// This is a built-in IME, so no need to warn.
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
break;
}
}
if (selImi == null) {
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
chkPref.setChecked(false);
if (mDialog == null) {
mDialog = (new AlertDialog.Builder(getActivity()))
.setTitle(android.R.string.dialog_alert_title)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setCancelable(true)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
chkPref.setChecked(true);
InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
InputMethodAndSubtypeEnabler.this,
mInputMethodProperties, id, true);
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
} else {
if (mDialog.isShowing()) {
mDialog.dismiss();
}
}
mDialog.setMessage(getResources().getString(
R.string.ime_security_warning,
selImi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
mDialog.show();
} else {
InputMethodAndSubtypeUtil.setSubtypesPreferenceEnabled(
this, mInputMethodProperties, id, false);
updateAutoSelectionCB();
}
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
}
private void onCreateIMM() {
InputMethodManager imm = (InputMethodManager) getSystemService(
Context.INPUT_METHOD_SERVICE);
// TODO: Change mInputMethodProperties to Map
mInputMethodProperties = imm.getInputMethodList();
}
private PreferenceScreen createPreferenceHierarchy() {
// Root
final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
final Context context = getActivity();
int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
for (int i = 0; i < N; ++i) {
final InputMethodInfo imi = mInputMethodProperties.get(i);
final int subtypeCount = imi.getSubtypeCount();
if (subtypeCount <= 1) continue;
final String imiId = imi.getId();
// Add this subtype to the list when no IME is specified or when the IME of this
// subtype is the specified IME.
if (!TextUtils.isEmpty(mInputMethodId) && !mInputMethodId.equals(imiId)) {
continue;
}
final PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(context);
root.addPreference(keyboardSettingsCategory);
final PackageManager pm = getPackageManager();
final CharSequence label = imi.loadLabel(pm);
keyboardSettingsCategory.setTitle(label);
keyboardSettingsCategory.setKey(imiId);
// TODO: Use toggle Preference if images are ready.
final CheckBoxPreference autoCB = new CheckBoxPreference(context);
mSubtypeAutoSelectionCBMap.put(imiId, autoCB);
keyboardSettingsCategory.addPreference(autoCB);
final PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(context);
activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
root.addPreference(activeInputMethodsCategory);
boolean isAutoSubtype = false;
CharSequence autoSubtypeLabel = null;
final ArrayList<Preference> subtypePreferences = new ArrayList<Preference>();
if (subtypeCount > 0) {
for (int j = 0; j < subtypeCount; ++j) {
final InputMethodSubtype subtype = imi.getSubtypeAt(j);
final CharSequence subtypeLabel = subtype.getDisplayName(context,
imi.getPackageName(), imi.getServiceInfo().applicationInfo);
if (subtype.overridesImplicitlyEnabledSubtype()) {
if (!isAutoSubtype) {
isAutoSubtype = true;
autoSubtypeLabel = subtypeLabel;
}
} else {
final CheckBoxPreference chkbxPref = new SubtypeCheckBoxPreference(
context, subtype.getLocale(), mSystemLocale);
chkbxPref.setKey(imiId + subtype.hashCode());
chkbxPref.setTitle(subtypeLabel);
subtypePreferences.add(chkbxPref);
}
}
Collections.sort(subtypePreferences);
for (int j = 0; j < subtypePreferences.size(); ++j) {
activeInputMethodsCategory.addPreference(subtypePreferences.get(j));
}
mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences);
}
if (isAutoSubtype) {
if (TextUtils.isEmpty(autoSubtypeLabel)) {
Log.w(TAG, "Title for auto subtype is empty.");
autoCB.setTitle("---");
} else {
autoCB.setTitle(autoSubtypeLabel);
}
} else {
autoCB.setTitle(R.string.use_system_language_to_select_input_method_subtypes);
}
}
return root;
}
private boolean isNoSubtypesExplicitlySelected(String imiId) {
boolean allSubtypesOff = true;
final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
for (Preference subtypePref: subtypePrefs) {
if (subtypePref instanceof CheckBoxPreference
&& ((CheckBoxPreference)subtypePref).isChecked()) {
allSubtypesOff = false;
break;
}
}
return allSubtypesOff;
}
private void setSubtypeAutoSelectionEnabled(String imiId, boolean autoSelectionEnabled) {
CheckBoxPreference autoSelectionCB = mSubtypeAutoSelectionCBMap.get(imiId);
if (autoSelectionCB == null) return;
autoSelectionCB.setChecked(autoSelectionEnabled);
final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
for (Preference subtypePref: subtypePrefs) {
if (subtypePref instanceof CheckBoxPreference) {
// When autoSelectionEnabled is true, all subtype prefs need to be disabled with
// implicitly checked subtypes. In case of false, all subtype prefs need to be
// enabled.
subtypePref.setEnabled(!autoSelectionEnabled);
if (autoSelectionEnabled) {
((CheckBoxPreference)subtypePref).setChecked(false);
}
}
}
if (autoSelectionEnabled) {
InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
mInputMethodProperties, mHaveHardKeyboard);
setCheckedImplicitlyEnabledSubtypes(imiId);
}
}
private void setCheckedImplicitlyEnabledSubtypes(String targetImiId) {
updateImplicitlyEnabledSubtypes(targetImiId, true);
}
private void clearImplicitlyEnabledSubtypes(String targetImiId) {
updateImplicitlyEnabledSubtypes(targetImiId, false);
}
private void updateImplicitlyEnabledSubtypes(String targetImiId, boolean check) {
// When targetImiId is null, apply to all subtypes of all IMEs
for (InputMethodInfo imi: mInputMethodProperties) {
String imiId = imi.getId();
if (targetImiId != null && !targetImiId.equals(imiId)) continue;
final CheckBoxPreference autoCB = mSubtypeAutoSelectionCBMap.get(imiId);
// No need to update implicitly enabled subtypes when the user has unchecked the
// "subtype auto selection".
if (autoCB == null || !autoCB.isChecked()) continue;
final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId);
final List<InputMethodSubtype> implicitlyEnabledSubtypes =
mImm.getEnabledInputMethodSubtypeList(imi, true);
if (subtypePrefs == null || implicitlyEnabledSubtypes == null) continue;
for (Preference subtypePref: subtypePrefs) {
if (subtypePref instanceof CheckBoxPreference) {
CheckBoxPreference cb = (CheckBoxPreference)subtypePref;
cb.setChecked(false);
if (check) {
for (InputMethodSubtype subtype: implicitlyEnabledSubtypes) {
String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode();
if (cb.getKey().equals(implicitlyEnabledSubtypePrefKey)) {
cb.setChecked(true);
break;
}
}
}
}
}
}
}
private void updateAutoSelectionCB() {
for (String imiId: mInputMethodAndSubtypePrefsMap.keySet()) {
setSubtypeAutoSelectionEnabled(imiId, isNoSubtypesExplicitlySelected(imiId));
}
setCheckedImplicitlyEnabledSubtypes(null);
}
private static class SubtypeCheckBoxPreference extends CheckBoxPreference {
private final boolean mIsSystemLocale;
private final boolean mIsSystemLanguage;
public SubtypeCheckBoxPreference(
Context context, String subtypeLocale, String systemLocale) {
super(context);
if (TextUtils.isEmpty(subtypeLocale)) {
mIsSystemLocale = false;
mIsSystemLanguage = false;
} else {
mIsSystemLocale = subtypeLocale.equals(systemLocale);
mIsSystemLanguage = mIsSystemLocale
|| subtypeLocale.startsWith(systemLocale.substring(0, 2));
}
}
@Override
public int compareTo(Preference p) {
if (p instanceof SubtypeCheckBoxPreference) {
final SubtypeCheckBoxPreference pref = ((SubtypeCheckBoxPreference)p);
final CharSequence t0 = getTitle();
final CharSequence t1 = pref.getTitle();
if (TextUtils.equals(t0, t1)) {
return 0;
}
if (mIsSystemLocale) {
return -1;
}
if (pref.mIsSystemLocale) {
return 1;
}
if (mIsSystemLanguage) {
return -1;
}
if (pref.mIsSystemLanguage) {
return 1;
}
if (TextUtils.isEmpty(t0)) {
return 1;
}
if (TextUtils.isEmpty(t1)) {
return -1;
}
return t0.toString().compareTo(t1.toString());
} else {
Log.w(TAG, "Illegal preference type.");
return -1;
}
}
}
}