summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/InputMethodManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java297
1 files changed, 230 insertions, 67 deletions
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 7d4faea..43c2292 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -27,6 +27,7 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputBindResult;
import com.android.server.EventLogTags;
+import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -91,7 +92,10 @@ import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.RadioButton;
+import android.widget.Switch;
import android.widget.TextView;
import java.io.File;
@@ -101,6 +105,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -113,7 +118,7 @@ import java.util.TreeMap;
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
static final boolean DEBUG = false;
- static final String TAG = "InputManagerService";
+ static final String TAG = "InputMethodManagerService";
static final int MSG_SHOW_IM_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
@@ -133,6 +138,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_UNBIND_METHOD = 3000;
static final int MSG_BIND_METHOD = 3010;
+ static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
+
static final long TIME_TO_RECONNECT = 10*1000;
static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
@@ -142,6 +149,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
+ private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
+ "EnabledWhenDefaultIsNotAsciiCapable";
+ private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
final Context mContext;
final Resources mRes;
@@ -151,6 +161,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final IWindowManager mIWindowManager;
final HandlerCaller mCaller;
private final InputMethodFileManager mFileManager;
+ private final InputMethodAndSubtypeListManager mImListManager;
+ private final HardKeyboardListener mHardKeyboardListener;
+ private final WindowManagerService mWindowManagerService;
final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
@@ -355,6 +368,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private AlertDialog.Builder mDialogBuilder;
private AlertDialog mSwitchingDialog;
+ private View mSwitchingDialogTitleView;
private InputMethodInfo[] mIms;
private int[] mSubtypeIds;
@@ -523,7 +537,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- public InputMethodManagerService(Context context) {
+ private class HardKeyboardListener
+ implements WindowManagerService.OnHardKeyboardStatusChangeListener {
+ @Override
+ public void onHardKeyboardStatusChange(boolean available, boolean enabled) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0, enabled ? 1 : 0));
+ }
+
+ public void handleHardKeyboardStatusChange(boolean available, boolean enabled) {
+ if (DEBUG) {
+ Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + ", enabled = "
+ + enabled);
+ }
+ synchronized(mMethodMap) {
+ if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
+ && mSwitchingDialog.isShowing()) {
+ mSwitchingDialogTitleView.findViewById(
+ com.android.internal.R.id.hard_keyboard_section).setVisibility(
+ available ? View.VISIBLE : View.GONE);
+ }
+ }
+ }
+ }
+
+ public InputMethodManagerService(Context context, WindowManagerService windowManager) {
mContext = context;
mRes = context.getResources();
mHandler = new Handler(this);
@@ -535,6 +573,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
handleMessage(msg);
}
});
+ mWindowManagerService = windowManager;
+ mHardKeyboardListener = new HardKeyboardListener();
mImeSwitcherNotification = new Notification();
mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default;
@@ -552,8 +592,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
mFileManager = new InputMethodFileManager(mMethodMap);
}
+ mImListManager = new InputMethodAndSubtypeListManager(context, this);
- (new MyPackageMonitor()).register(mContext, true);
+ (new MyPackageMonitor()).register(mContext, null, true);
IntentFilter screenOnOffFilt = new IntentFilter();
screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
@@ -627,6 +668,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
updateImeWindowStatusLocked();
mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
com.android.internal.R.bool.show_ongoing_ime_switcher);
+ if (mShowOngoingImeSwitcherForPhones) {
+ mWindowManagerService.setOnHardKeyboardStatusChangeListener(
+ mHardKeyboardListener);
+ }
try {
startInputInnerLocked();
} catch (RuntimeException e) {
@@ -1126,6 +1171,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private boolean needsToShowImeSwitchOngoingNotification() {
if (!mShowOngoingImeSwitcherForPhones) return false;
+ if (isScreenLocked()) return false;
synchronized (mMethodMap) {
List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
final int N = imis.size();
@@ -1735,6 +1781,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
+ public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+ synchronized (mMethodMap) {
+ final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
+ onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+ if (nextSubtype == null) {
+ return false;
+ }
+ setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+ return true;
+ }
+ }
+
+ @Override
public InputMethodSubtype getLastInputMethodSubtype() {
synchronized (mMethodMap) {
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -1974,6 +2033,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Slog.w(TAG, "Client died receiving input method " + args.arg2);
}
return true;
+
+ // --------------------------------------------------------------
+ case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
+ mHardKeyboardListener.handleHardKeyboardStatusChange(
+ msg.arg1 == 1, msg.arg2 == 1);
+ return true;
}
return false;
}
@@ -2129,15 +2194,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.startActivity(intent);
}
+ private boolean isScreenLocked() {
+ return mKeyguardManager != null
+ && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
+ }
private void showInputMethodMenuInternal(boolean showSubtypes) {
if (DEBUG) Slog.v(TAG, "Show switching menu");
final Context context = mContext;
final PackageManager pm = context.getPackageManager();
- final boolean isScreenLocked = mKeyguardManager != null
- && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
+ final boolean isScreenLocked = isScreenLocked();
- String lastInputMethodId = Settings.Secure.getString(context
+ final String lastInputMethodId = Settings.Secure.getString(context
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
@@ -2151,60 +2219,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
hideInputMethodMenuLocked();
- final TreeMap<InputMethodInfo, List<InputMethodSubtype>> sortedImmis =
- new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
- new Comparator<InputMethodInfo>() {
- @Override
- public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
- if (imi2 == null) return 0;
- if (imi1 == null) return 1;
- if (pm == null) {
- return imi1.getId().compareTo(imi2.getId());
- }
- CharSequence imiId1 = imi1.loadLabel(pm) + "/" + imi1.getId();
- CharSequence imiId2 = imi2.loadLabel(pm) + "/" + imi2.getId();
- return imiId1.toString().compareTo(imiId2.toString());
- }
- });
-
- sortedImmis.putAll(immis);
-
- final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
+ final List<ImeSubtypeListItem> imList =
+ mImListManager.getSortedInputMethodAndSubtypeList(
+ showSubtypes, mInputShown, isScreenLocked);
- for (InputMethodInfo imi : sortedImmis.keySet()) {
- if (imi == null) continue;
- List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
- HashSet<String> enabledSubtypeSet = new HashSet<String>();
- for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
- enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
- }
- ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
- final CharSequence imeLabel = imi.loadLabel(pm);
- if (showSubtypes && enabledSubtypeSet.size() > 0) {
- final int subtypeCount = imi.getSubtypeCount();
- if (DEBUG) {
- Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
- }
- for (int j = 0; j < subtypeCount; ++j) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(j);
- final String subtypeHashCode = String.valueOf(subtype.hashCode());
- // We show all enabled IMEs and subtypes when an IME is shown.
- if (enabledSubtypeSet.contains(subtypeHashCode)
- && ((mInputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
- final CharSequence subtypeLabel =
- subtype.overridesImplicitlyEnabledSubtype() ? null
- : subtype.getDisplayName(context, imi.getPackageName(),
- imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
-
- // Removing this subtype from enabledSubtypeSet because we no longer
- // need to add an entry of this subtype to imList to avoid duplicated
- // entries.
- enabledSubtypeSet.remove(subtypeHashCode);
- }
- }
- } else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
+ if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
+ final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype();
+ if (currentSubtype != null) {
+ final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
+ lastInputMethodSubtypeId =
+ getSubtypeIdFromHashCode(currentImi, currentSubtype.hashCode());
}
}
@@ -2225,12 +2249,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
}
-
final TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.DialogPreference,
com.android.internal.R.attr.alertDialogStyle, 0);
mDialogBuilder = new AlertDialog.Builder(context)
- .setTitle(com.android.internal.R.string.select_input_method)
.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
@@ -2240,6 +2262,29 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
.setIcon(a.getDrawable(
com.android.internal.R.styleable.DialogPreference_dialogTitle));
a.recycle();
+ final LayoutInflater inflater =
+ (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final View tv = inflater.inflate(
+ com.android.internal.R.layout.input_method_switch_dialog_title, null);
+ mDialogBuilder.setCustomTitle(tv);
+
+ // Setup layout for a toggle switch of the hardware keyboard
+ mSwitchingDialogTitleView = tv;
+ mSwitchingDialogTitleView.findViewById(
+ com.android.internal.R.id.hard_keyboard_section).setVisibility(
+ mWindowManagerService.isHardKeyboardAvailable() ?
+ View.VISIBLE : View.GONE);
+ final Switch hardKeySwitch = ((Switch)mSwitchingDialogTitleView.findViewById(
+ com.android.internal.R.id.hard_keyboard_switch));
+ hardKeySwitch.setChecked(mWindowManagerService.isHardKeyboardEnabled());
+ hardKeySwitch.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(
+ CompoundButton buttonView, boolean isChecked) {
+ mWindowManagerService.setHardKeyboardEnabled(isChecked);
+ }
+ });
final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(context,
com.android.internal.R.layout.simple_list_item_2_single_choice, imList,
@@ -2517,7 +2562,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap =
new HashMap<String, InputMethodSubtype>();
final int N = subtypes.size();
- boolean containsKeyboardSubtype = false;
for (int i = 0; i < N; ++i) {
// scan overriding implicitly enabled subtypes.
InputMethodSubtype subtype = subtypes.get(i);
@@ -2551,15 +2595,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!systemLocale.equals(locale)) continue;
}
applicableModeAndSubtypesMap.put(mode, subtype);
- if (!containsKeyboardSubtype
- && SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
- containsKeyboardSubtype = true;
- }
}
}
+ final InputMethodSubtype keyboardSubtype
+ = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD);
final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
applicableModeAndSubtypesMap.values());
- if (!containsKeyboardSubtype) {
+ if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) {
+ for (int i = 0; i < N; ++i) {
+ final InputMethodSubtype subtype = subtypes.get(i);
+ final String mode = subtype.getMode();
+ if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey(
+ TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) {
+ applicableSubtypes.add(subtype);
+ }
+ }
+ }
+ if (keyboardSubtype == null) {
InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true);
if (lastResortKeyboardSubtype != null) {
@@ -2812,6 +2864,117 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ private static class InputMethodAndSubtypeListManager {
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final InputMethodManagerService mImms;
+ public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) {
+ mContext = context;
+ mPm = context.getPackageManager();
+ mImms = imms;
+ }
+
+ private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
+ new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+ new Comparator<InputMethodInfo>() {
+ @Override
+ public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
+ if (imi2 == null) return 0;
+ if (imi1 == null) return 1;
+ if (mPm == null) {
+ return imi1.getId().compareTo(imi2.getId());
+ }
+ CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId();
+ CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId();
+ return imiId1.toString().compareTo(imiId2.toString());
+ }
+ });
+
+ public ImeSubtypeListItem getNextInputMethod(
+ boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return null;
+ }
+ final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList();
+ if (imList.size() <= 1) {
+ return null;
+ }
+ final int N = imList.size();
+ final int currentSubtypeId = subtype != null
+ ? mImms.getSubtypeIdFromHashCode(imi, subtype.hashCode())
+ : NOT_A_SUBTYPE_ID;
+ for (int i = 0; i < N; ++i) {
+ final ImeSubtypeListItem isli = imList.get(i);
+ if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
+ if (!onlyCurrentIme) {
+ return imList.get((i + 1) % N);
+ }
+ for (int j = 0; j < N - 1; ++j) {
+ final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
+ if (candidate.mImi.equals(imi)) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
+ return getSortedInputMethodAndSubtypeList(true, false, false);
+ }
+
+ public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes,
+ boolean inputShown, boolean isScreenLocked) {
+ final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
+ final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
+ mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
+ if (immis == null || immis.size() == 0) {
+ return Collections.emptyList();
+ }
+ mSortedImmis.clear();
+ mSortedImmis.putAll(immis);
+ for (InputMethodInfo imi : mSortedImmis.keySet()) {
+ if (imi == null) continue;
+ List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
+ HashSet<String> enabledSubtypeSet = new HashSet<String>();
+ for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
+ enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
+ }
+ ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi);
+ final CharSequence imeLabel = imi.loadLabel(mPm);
+ if (showSubtypes && enabledSubtypeSet.size() > 0) {
+ final int subtypeCount = imi.getSubtypeCount();
+ if (DEBUG) {
+ Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+ }
+ for (int j = 0; j < subtypeCount; ++j) {
+ final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+ final String subtypeHashCode = String.valueOf(subtype.hashCode());
+ // We show all enabled IMEs and subtypes when an IME is shown.
+ if (enabledSubtypeSet.contains(subtypeHashCode)
+ && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
+ final CharSequence subtypeLabel =
+ subtype.overridesImplicitlyEnabledSubtype() ? null
+ : subtype.getDisplayName(mContext, imi.getPackageName(),
+ imi.getServiceInfo().applicationInfo);
+ imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j));
+
+ // Removing this subtype from enabledSubtypeSet because we no longer
+ // need to add an entry of this subtype to imList to avoid duplicated
+ // entries.
+ enabledSubtypeSet.remove(subtypeHashCode);
+ }
+ }
+ } else {
+ imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID));
+ }
+ }
+ return imList;
+ }
+ }
+
/**
* Utility class for putting and getting settings for InputMethod
* TODO: Move all putters and getters of settings to this class.