Merge "Add an API to get shortcut IMEs"
diff --git a/api/current.xml b/api/current.xml
index 42cdaf2..4025f3c 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -156003,22 +156003,22 @@
visibility="public"
>
</field>
-<field name="GROUP_VISIBLE"
+<field name="GROUP_IS_READ_ONLY"
type="java.lang.String"
transient="false"
volatile="false"
- value=""group_visible""
+ value=""group_is_read_only""
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="GROUP_IS_READ_ONLY"
+<field name="GROUP_VISIBLE"
type="java.lang.String"
transient="false"
volatile="false"
- value=""group_is_read_only""
+ value=""group_visible""
static="true"
final="true"
deprecated="not deprecated"
@@ -220894,6 +220894,17 @@
visibility="public"
>
</method>
+<method name="getShortcutInputMethodsAndSubtypes"
+ return="java.util.List<android.util.Pair<android.view.inputmethod.InputMethodInfo, android.view.inputmethod.InputMethodSubtype>>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="hideSoftInputFromInputMethod"
return="void"
abstract="false"
@@ -248473,7 +248484,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fe55f34..a5f3ade 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -47,6 +48,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -1452,6 +1454,38 @@
}
}
+ public List<Pair<InputMethodInfo, InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() {
+ synchronized (mH) {
+ List<Pair<InputMethodInfo, InputMethodSubtype>> ret =
+ new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
+ try {
+ // TODO: We should change the return type from List<Object> to List<Parcelable>
+ List<Object> info = mService.getShortcutInputMethodsAndSubtypes();
+ // "info" has imi1, subtype1, imi2, subtype2, imi3, subtype3,..... in the list
+ Object imi;
+ Object subtype;
+ if (info != null && info.size() > 0) {
+ final int N = info.size();
+ if (N % 2 == 0) {
+ for (int i = 0; i < N;) {
+ if ((imi = info.get(i++)) instanceof InputMethodInfo) {
+ subtype = info.get(i++);
+ ret.add(new Pair<InputMethodInfo, InputMethodSubtype> (
+ (InputMethodInfo)imi,
+ (subtype instanceof InputMethodSubtype) ?
+ (InputMethodSubtype)subtype : null));
+ }
+ }
+ } else {
+ Log.w(TAG, "The size of list was illegal.");
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ return ret;
+ }
+ }
public boolean switchToLastInputMethod(IBinder imeToken) {
synchronized (mH) {
try {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 63d05ec..125b210 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -32,6 +32,9 @@
List<InputMethodInfo> getInputMethodList();
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in InputMethodInfo imi);
+ // TODO: We should change the return type from List to List<Parcelable>
+ // Currently there is a bug that aidl doesn't accept List<Parcelable>
+ List getShortcutInputMethodsAndSubtypes();
void addClient(in IInputMethodClient client,
in IInputContext inputContext, int uid, int pid);
void removeClient(in IInputMethodClient client);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index e14345b..b2cc6bd 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -55,6 +55,7 @@
import android.os.IInterface;
import android.os.Message;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -120,6 +121,9 @@
// If IME doesn't support the system locale, the default subtype will be the first defined one.
private static final int DEFAULT_SUBTYPE_ID = 0;
+ private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
+ private static final String SUBTYPE_MODE_VOICE = "voice";
+
final Context mContext;
final Handler mHandler;
final InputMethodSettings mSettings;
@@ -235,6 +239,11 @@
*/
private InputMethodSubtype mCurrentSubtype;
+ // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
+ private List<Pair<InputMethodInfo, InputMethodSubtype>> mShortcutInputMethodsAndSubtypes;
+ // This list is used for returning the pairs of InputMethodInfo and InputMethodSubtype through
+ // aidl. This list has imi1, subtype1 imi2, subtype2...
+ private List mShortcutInputMethodsAndSubtypesObjectList;
/**
* Set to true if our ServiceConnection is currently actively bound to
@@ -983,6 +992,7 @@
mCurMethodId = null;
unbindCurrentMethodLocked(true);
}
+ mShortcutInputMethodsAndSubtypes = null;
} else {
// There is no longer an input method set, so stop any current one.
mCurMethodId = null;
@@ -1910,25 +1920,31 @@
return NOT_A_SUBTYPE_ID;
}
- // If there are no selected subtypes, tries finding the most applicable one according to the
- // current system locale
- private int findApplicableSubtypeLocked(String id) {
- InputMethodInfo imi = mMethodMap.get(id);
- if (imi == null) {
- return NOT_A_SUBTYPE_ID;
- }
- ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+ /**
+ * If there are no selected subtypes, tries finding the most applicable one according to the
+ * given locale.
+ * @param subtypes this function will search the most applicable subtype in subtypes
+ * @param mode subtypes will be filtered by mode
+ * @param locale subtypes will be filtered by locale
+ * @param defaultSubtypeId if this function can't find the most applicable subtype, it will
+ * return defaultSubtypeId
+ * @return the most applicable subtypeId
+ */
+ private int findLastResortApplicableSubtypeLocked(
+ List<InputMethodSubtype> subtypes, String mode, String locale, int defaultSubtypeId) {
if (subtypes == null || subtypes.size() == 0) {
return NOT_A_SUBTYPE_ID;
}
- final String locale = mContext.getResources().getConfiguration().locale.toString();
+ if (TextUtils.isEmpty(locale)) {
+ locale = mContext.getResources().getConfiguration().locale.toString();
+ }
final String language = locale.substring(0, 2);
boolean partialMatchFound = false;
- int applicableSubtypeId = DEFAULT_SUBTYPE_ID;
+ int applicableSubtypeId = defaultSubtypeId;
for (int i = 0; i < subtypes.size(); ++i) {
final String subtypeLocale = subtypes.get(i).getLocale();
- // An applicable subtype should be a keyboard subtype
- if (subtypes.get(i).getMode().equalsIgnoreCase("keyboard")) {
+ // An applicable subtype should match "mode".
+ if (subtypes.get(i).getMode().equalsIgnoreCase(mode)) {
if (locale.equals(subtypeLocale)) {
// Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
applicableSubtypeId = i;
@@ -1950,34 +1966,129 @@
return applicableSubtypeId;
}
+ // If there are no selected shortcuts, tries finding the most applicable ones.
+ private Pair<InputMethodInfo, InputMethodSubtype>
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
+ List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ InputMethodInfo mostApplicableIMI = null;
+ int mostApplicableSubtypeId = NOT_A_SUBTYPE_ID;
+ boolean foundInSystemIME = false;
+
+ // Search applicable subtype for each InputMethodInfo
+ for (InputMethodInfo imi: imis) {
+ int subtypeId = NOT_A_SUBTYPE_ID;
+ if (mCurrentSubtype != null) {
+ // 1. Search with the current subtype's locale and the enabled subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(
+ mSettings.getEnabledInputMethodSubtypeListLocked(
+ imi), mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ // 2. Search with the current subtype's locale and all subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ mode, mCurrentSubtype.getLocale(), NOT_A_SUBTYPE_ID);
+ }
+ }
+ // 3. Search with the system locale and the enabled subtypes
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ subtypeId = findLastResortApplicableSubtypeLocked(
+ mSettings.getEnabledInputMethodSubtypeListLocked(
+ imi), mode, null, NOT_A_SUBTYPE_ID);
+ }
+ if (subtypeId == NOT_A_SUBTYPE_ID) {
+ // 4. Search with the system locale and all subtypes
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ mode, null, NOT_A_SUBTYPE_ID);
+ }
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ if (imi.getId().equals(mCurMethodId)) {
+ // The current input method is the most applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ break;
+ } else if ((imi.getServiceInfo().applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ // The system input method is 2nd applicable IME.
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ foundInSystemIME = true;
+ } else if (!foundInSystemIME) {
+ mostApplicableIMI = imi;
+ mostApplicableSubtypeId = subtypeId;
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.w(TAG, "Most applicable shortcut input method subtype was:"
+ + mostApplicableIMI.getId() + "," + mostApplicableSubtypeId);
+ }
+ if (mostApplicableIMI != null && mostApplicableSubtypeId != NOT_A_SUBTYPE_ID) {
+ ArrayList<Parcelable> ret = new ArrayList<Parcelable>(2);
+ return new Pair<InputMethodInfo, InputMethodSubtype> (mostApplicableIMI,
+ mostApplicableIMI.getSubtypes().get(mostApplicableSubtypeId));
+ } else {
+ return null;
+ }
+ }
+
/**
* @return Return the current subtype of this input method.
*/
public InputMethodSubtype getCurrentInputMethodSubtype() {
+ boolean subtypeIsSelected = false;
+ try {
+ subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
+ } catch (SettingNotFoundException e) {
+ }
synchronized (mMethodMap) {
- boolean subtypeIsSelected = false;
- try {
- subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
- } catch (SettingNotFoundException e) {
- }
if (!subtypeIsSelected || mCurrentSubtype == null) {
- String lastInputMethodId =
- Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
+ String lastInputMethodId = Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (subtypeId == NOT_A_SUBTYPE_ID) {
- subtypeId = findApplicableSubtypeLocked(lastInputMethodId);
+ InputMethodInfo imi = mMethodMap.get(lastInputMethodId);
+ if (imi != null) {
+ // If there are no selected subtypes, the framework will try to find
+ // the most applicable subtype from all subtypes whose mode is
+ // SUBTYPE_MODE_KEYBOARD. This is an exceptional case, so we will hardcode
+ // the mode.
+ subtypeId = findLastResortApplicableSubtypeLocked(imi.getSubtypes(),
+ SUBTYPE_MODE_KEYBOARD, null, DEFAULT_SUBTYPE_ID);
+ }
}
if (subtypeId != NOT_A_SUBTYPE_ID) {
mCurrentSubtype =
mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
+ } else {
+ mCurrentSubtype = null;
}
}
return mCurrentSubtype;
}
}
+ // TODO: We should change the return type from List to List<Parcelable>
+ public List getShortcutInputMethodsAndSubtypes() {
+ synchronized (mMethodMap) {
+ if (mShortcutInputMethodsAndSubtypesObjectList != null) {
+ // If there are no selected shortcut subtypes, the framework will try to find
+ // the most applicable subtype from all subtypes whose mode is
+ // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
+ mShortcutInputMethodsAndSubtypes =
+ new ArrayList<Pair<InputMethodInfo, InputMethodSubtype>>();
+ mShortcutInputMethodsAndSubtypes.add(
+ findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
+ SUBTYPE_MODE_VOICE));
+ mShortcutInputMethodsAndSubtypesObjectList = new ArrayList<Parcelable>();
+ for (Pair ime: mShortcutInputMethodsAndSubtypes) {
+ mShortcutInputMethodsAndSubtypesObjectList.add(ime.first);
+ mShortcutInputMethodsAndSubtypesObjectList.add(ime.second);
+ }
+ }
+ return mShortcutInputMethodsAndSubtypesObjectList;
+ }
+ }
+
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mMethodMap) {
if (subtype != null && mCurMethodId != null) {