summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml267
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java1
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java10
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java42
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java6
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java135
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java12
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.aidl19
-rw-r--r--core/java/android/view/inputmethod/InputMethodSubtype.java149
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl3
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl1
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java206
13 files changed, 607 insertions, 251 deletions
diff --git a/api/current.xml b/api/current.xml
index 00d7fb0..07aabed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -89810,6 +89810,19 @@
visibility="public"
>
</method>
+<method name="onCurrentInputMethodSubtypeChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="newSubtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
<method name="onDisplayCompletions"
return="void"
abstract="false"
@@ -90413,6 +90426,19 @@
<parameter name="binding" type="android.view.inputmethod.InputBinding">
</parameter>
</method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
<method name="hideSoftInput"
return="void"
abstract="false"
@@ -156779,6 +156805,17 @@
visibility="public"
>
</field>
+<field name="SELECTED_INPUT_METHOD_SUBTYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;selected_input_method_subtype&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SETTINGS_CLASSNAME"
type="java.lang.String"
transient="false"
@@ -213754,6 +213791,19 @@
<parameter name="binding" type="android.view.inputmethod.InputBinding">
</parameter>
</method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
<method name="createSession"
return="void"
abstract="true"
@@ -214078,7 +214128,7 @@
>
</method>
<method name="getSubtypes"
- return="java.util.ArrayList&lt;android.view.inputmethod.InputMethodInfo.InputMethodSubtype&gt;"
+ return="java.util.ArrayList&lt;android.view.inputmethod.InputMethodSubtype&gt;"
abstract="false"
native="false"
synchronized="false"
@@ -214140,108 +214190,6 @@
>
</field>
</class>
-<class name="InputMethodInfo.InputMethodSubtype"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getExtraValue"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getIconId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocale"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMode"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="parcelableFlags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
<class name="InputMethodManager"
extends="java.lang.Object"
abstract="false"
@@ -214462,6 +214410,17 @@
visibility="public"
>
</method>
+<method name="showInputMethodSubtypePicker"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="showSoftInput"
return="boolean"
abstract="false"
@@ -214876,6 +214835,108 @@
</parameter>
</method>
</interface>
+<class name="InputMethodSubtype"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExtraValue"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIconResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getModeResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNameResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
</package>
<package name="android.webkit"
>
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3619653..27af013 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -177,6 +177,7 @@ public abstract class AbstractInputMethodService extends Service
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
* calls on your input method.
*/
+ @Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 35fd46f..24ea7d2 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -36,6 +36,7 @@ import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import android.view.inputmethod.InputMethodSubtype;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -64,6 +65,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_REVOKE_SESSION = 50;
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
+ private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
final WeakReference<AbstractInputMethodService> mTarget;
final HandlerCaller mCaller;
@@ -178,6 +180,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_HIDE_SOFT_INPUT:
inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
+ case DO_CHANGE_INPUTMETHOD_SUBTYPE:
+ inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
+ return;
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -267,4 +272,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
flags, resultReceiver));
}
+
+ public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
+ subtype));
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1a261d3..6089013 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,6 +52,7 @@ import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.FrameLayout;
@@ -270,7 +271,7 @@ public class InputMethodService extends AbstractInputMethodService {
final Insets mTmpInsets = new Insets();
final int[] mTmpLocation = new int[2];
-
+
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
new ViewTreeObserver.OnComputeInternalInsetsListener() {
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
@@ -394,8 +395,12 @@ public class InputMethodService extends AbstractInputMethodService {
: InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
}
}
+
+ public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+ onCurrentInputMethodSubtypeChanged(subtype);
+ }
}
-
+
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -541,6 +546,7 @@ public class InputMethodService extends AbstractInputMethodService {
* will typically call it in your constructor with the resource ID
* of your custom theme.
*/
+ @Override
public void setTheme(int theme) {
if (mWindow != null) {
throw new IllegalStateException("Must be called before onCreate()");
@@ -558,7 +564,7 @@ public class InputMethodService extends AbstractInputMethodService {
initViews();
mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
}
-
+
/**
* This is a hook that subclasses can use to perform initialization of
* their interface. It is called for you prior to any of your UI objects
@@ -567,14 +573,14 @@ public class InputMethodService extends AbstractInputMethodService {
*/
public void onInitializeInterface() {
}
-
+
void initialize() {
if (!mInitialized) {
mInitialized = true;
onInitializeInterface();
}
}
-
+
void initViews() {
mInitialized = false;
mWindowCreated = false;
@@ -610,7 +616,7 @@ public class InputMethodService extends AbstractInputMethodService {
mCandidatesFrame.setVisibility(mCandidatesVisibility);
mInputFrame.setVisibility(View.GONE);
}
-
+
@Override public void onDestroy() {
super.onDestroy();
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
@@ -677,6 +683,7 @@ public class InputMethodService extends AbstractInputMethodService {
* Implement to return our standard {@link InputMethodImpl}. Subclasses
* can override to provide their own customized version.
*/
+ @Override
public AbstractInputMethodImpl onCreateInputMethodInterface() {
return new InputMethodImpl();
}
@@ -685,6 +692,7 @@ public class InputMethodService extends AbstractInputMethodService {
* Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
* can override to provide their own customized version.
*/
+ @Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
}
@@ -1656,6 +1664,7 @@ public class InputMethodService extends AbstractInputMethodService {
return doMovementKey(keyCode, event, MOVEMENT_UP);
}
+ @Override
public boolean onTrackballEvent(MotionEvent event) {
return false;
}
@@ -1694,7 +1703,7 @@ public class InputMethodService extends AbstractInputMethodService {
dy = count;
break;
}
- onExtractedCursorMovement(dx, dy);
+ onExtractedCursorMovement(dx, dy);
}
boolean doMovementKey(int keyCode, KeyEvent event, int count) {
@@ -2064,7 +2073,24 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
}
-
+
+ // TODO: Handle the subtype change event
+ /**
+ * Called when the subtype was changed.
+ * @param newSubtype the subtype which is being changed to.
+ */
+ protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+ if (DEBUG) {
+ int nameResId = newSubtype.getNameResId();
+ int modeResId = newSubtype.getModeResId();
+ String output = "changeInputMethodSubtype:"
+ + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
+ + (modeResId == 0 ? "<none>" : getString(modeResId)) + ","
+ + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
+ Log.v(TAG, "--- " + output);
+ }
+ }
+
/**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 51dc6ae..e98fa26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2356,6 +2356,13 @@ public final class Settings {
public static final String DEFAULT_INPUT_METHOD = "default_input_method";
/**
+ * Setting to record the input method subtype used by default, holding the ID
+ * of the desired method.
+ */
+ public static final String SELECTED_INPUT_METHOD_SUBTYPE =
+ "selected_input_method_subtype";
+
+ /**
* Whether the device has been provisioned (0 = false, 1 = true)
*/
public static final String DEVICE_PROVISIONED = "device_provisioned";
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 2ddf5f8..5ab3d34 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -219,4 +219,10 @@ public interface InputMethod {
* {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
*/
public void hideSoftInput(int flags, ResultReceiver resultReceiver);
+
+ /**
+ * Notify that the input method subtype is being changed in the same input method.
+ * @param subtype New subtype of the notified input method
+ */
+ public void changeInputMethodSubtype(InputMethodSubtype subtype);
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 68e231c..54102f6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -44,7 +44,7 @@ import java.util.ArrayList;
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
-
+
/**
* The Service that implements this input method component.
*/
@@ -71,102 +71,6 @@ public final class InputMethodInfo implements Parcelable {
final int mIsDefaultResId;
/**
- * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
- * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
- * IME switch. The subtype allows the system to call the specified subtype of IME directly.
- */
- public static class InputMethodSubtype implements Parcelable {
- private final String mSubtypeName;
- private final int mSubtypeIconId;
- private final String mSubtypeLocale;
- private final String mSubtypeMode;
- private final String mSubtypeExtraValue;
-
- /**
- * Constructor
- * @param name The name of the subtype
- * @param iconId The icon of the subtype
- * @param locale The locale supported by the subtype
- * @param mode The mode supported by the subtype
- * @param extraValue The extra value of the subtype
- */
- InputMethodSubtype(String name, int iconId, String locale, String mode,
- String extraValue) {
- mSubtypeName = name;
- mSubtypeIconId = iconId;
- mSubtypeLocale = locale;
- mSubtypeMode = mode;
- mSubtypeExtraValue = extraValue;
- }
-
- InputMethodSubtype(Parcel source) {
- mSubtypeName = source.readString();
- mSubtypeIconId = source.readInt();
- mSubtypeLocale = source.readString();
- mSubtypeMode = source.readString();
- mSubtypeExtraValue = source.readString();
- }
-
- /**
- * @return the name of the subtype
- */
- public String getName() {
- return mSubtypeName;
- }
-
- /**
- * @return the icon of the subtype
- */
- public int getIconId() {
- return mSubtypeIconId;
- }
-
- /**
- * @return the locale of the subtype
- */
- public String getLocale() {
- return mSubtypeLocale;
- }
-
- /**
- * @return the mode of the subtype
- */
- public String getMode() {
- return mSubtypeMode;
- }
-
- /**
- * @return the extra value of the subtype
- */
- public String getExtraValue() {
- return mSubtypeExtraValue;
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int parcelableFlags) {
- dest.writeString(mSubtypeName);
- dest.writeInt(mSubtypeIconId);
- dest.writeString(mSubtypeLocale);
- dest.writeString(mSubtypeMode);
- dest.writeString(mSubtypeExtraValue);
- }
-
- public static final Parcelable.Creator<InputMethodSubtype> CREATOR
- = new Parcelable.Creator<InputMethodSubtype>() {
- public InputMethodSubtype createFromParcel(Parcel source) {
- return new InputMethodSubtype(source);
- }
-
- public InputMethodSubtype[] newArray(int size) {
- return new InputMethodSubtype[size];
- }
- };
- }
-
- /**
* The array of the subtypes.
*/
private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>();
@@ -223,24 +127,27 @@ public final class InputMethodInfo implements Parcelable {
// Parse all subtypes
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
- nodeName = parser.getName();
- if (!"subtype".equals(nodeName)) {
- throw new XmlPullParserException(
- "Meta-data in input-method does not start with subtype tag");
+ if (type == XmlPullParser.START_TAG) {
+ nodeName = parser.getName();
+ if (!"subtype".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data in input-method does not start with subtype tag");
+ }
+ final TypedArray a = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.InputMethod_Subtype);
+ InputMethodSubtype subtype = new InputMethodSubtype(
+ a.getResourceId(com.android.internal.R.styleable
+ .InputMethod_Subtype_label, 0),
+ a.getResourceId(com.android.internal.R.styleable
+ .InputMethod_Subtype_icon, 0),
+ a.getString(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeLocale),
+ a.getResourceId(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeMode, 0),
+ a.getString(com.android.internal.R.styleable
+ .InputMethod_Subtype_imeSubtypeExtraValue));
+ mSubtypes.add(subtype);
}
- final TypedArray a = res.obtainAttributes(
- attrs, com.android.internal.R.styleable.InputMethod_Subtype);
- InputMethodSubtype subtype = new InputMethodSubtype(
- a.getString(com.android.internal.R.styleable.InputMethod_Subtype_label),
- a.getResourceId(
- com.android.internal.R.styleable.InputMethod_Subtype_icon, 0),
- a.getString(com.android.internal.R.styleable
- .InputMethod_Subtype_imeSubtypeLocale),
- a.getString(com.android.internal.R.styleable
- .InputMethod_Subtype_imeSubtypeMode),
- a.getString(com.android.internal.R.styleable
- .InputMethod_Subtype_imeSubtypeExtraValue));
- mSubtypes.add(subtype);
}
} catch (NameNotFoundException e) {
throw new XmlPullParserException(
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e30687f..8bd3298 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1380,7 +1380,7 @@ public final class InputMethodManager {
}
}
}
-
+
public void showInputMethodPicker() {
synchronized (mH) {
try {
@@ -1391,6 +1391,16 @@ public final class InputMethodManager {
}
}
+ public void showInputMethodSubtypePicker() {
+ synchronized (mH) {
+ try {
+ mService.showInputMethodSubtypePickerFromClient(mClient);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.aidl b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
new file mode 100644
index 0000000..ed82b84
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.view.inputmethod;
+
+parcelable InputMethodSubtype;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
new file mode 100644
index 0000000..a1ed044
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -0,0 +1,149 @@
+/*
+ * 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 android.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Information given to an {@link InputMethod} about a client connecting
+ * to it.
+ */
+/**
+ * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
+ * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
+ * IME switch. The subtype allows the system to call the specified subtype of IME directly.
+ */
+public final class InputMethodSubtype implements Parcelable {
+ private final int mSubtypeNameResId;
+ private final int mSubtypeIconResId;
+ private final String mSubtypeLocale;
+ private final int mSubtypeModeResId;
+ private final String mSubtypeExtraValue;
+ private final int mSubtypeHashCode;
+
+ /**
+ * Constructor
+ * @param nameId The name of the subtype
+ * @param iconId The icon of the subtype
+ * @param locale The locale supported by the subtype
+ * @param modeId The mode supported by the subtype
+ * @param extraValue The extra value of the subtype
+ */
+ InputMethodSubtype(int nameId, int iconId, String locale, int modeId, String extraValue) {
+ mSubtypeNameResId = nameId;
+ mSubtypeIconResId = iconId;
+ mSubtypeLocale = locale;
+ mSubtypeModeResId = modeId;
+ mSubtypeExtraValue = extraValue;
+ mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+ mSubtypeModeResId, mSubtypeExtraValue);
+ }
+
+ InputMethodSubtype(Parcel source) {
+ mSubtypeNameResId = source.readInt();
+ mSubtypeIconResId = source.readInt();
+ mSubtypeLocale = source.readString();
+ mSubtypeModeResId = source.readInt();
+ mSubtypeExtraValue = source.readString();
+ mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+ mSubtypeModeResId, mSubtypeExtraValue);
+ }
+
+ /**
+ * @return the name of the subtype
+ */
+ public int getNameResId() {
+ return mSubtypeNameResId;
+ }
+
+ /**
+ * @return the icon of the subtype
+ */
+ public int getIconResId() {
+ return mSubtypeIconResId;
+ }
+
+ /**
+ * @return the locale of the subtype
+ */
+ public String getLocale() {
+ return mSubtypeLocale;
+ }
+
+ /**
+ * @return the mode of the subtype
+ */
+ public int getModeResId() {
+ return mSubtypeModeResId;
+ }
+
+ /**
+ * @return the extra value of the subtype
+ */
+ public String getExtraValue() {
+ return mSubtypeExtraValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return mSubtypeHashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof InputMethodSubtype) {
+ InputMethodSubtype subtype = (InputMethodSubtype) o;
+ return (subtype.getNameResId() == getNameResId())
+ && (subtype.getModeResId() == getModeResId())
+ && (subtype.getIconResId() == getIconResId())
+ && (subtype.getLocale().equals(getLocale()))
+ && (subtype.getExtraValue().equals(getExtraValue()));
+ }
+ return false;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeInt(mSubtypeNameResId);
+ dest.writeInt(mSubtypeIconResId);
+ dest.writeString(mSubtypeLocale);
+ dest.writeInt(mSubtypeModeResId);
+ dest.writeString(mSubtypeExtraValue);
+ }
+
+ public static final Parcelable.Creator<InputMethodSubtype> CREATOR
+ = new Parcelable.Creator<InputMethodSubtype>() {
+ public InputMethodSubtype createFromParcel(Parcel source) {
+ return new InputMethodSubtype(source);
+ }
+
+ public InputMethodSubtype[] newArray(int size) {
+ return new InputMethodSubtype[size];
+ }
+ };
+
+ private static int hashCodeInternal(int nameResId, int iconResId, String locale,
+ int modeResId, String extraValue) {
+ return Arrays.hashCode(new Object[] {nameResId, iconResId, locale, modeResId, extraValue});
+ }
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8ff18ed..c7fcab8 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
@@ -52,4 +53,6 @@ oneway interface IInputMethod {
void showSoftInput(int flags, in ResultReceiver resultReceiver);
void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+
+ void changeInputMethodSubtype(in InputMethodSubtype subtype);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index adec0a7..d012b0f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -48,6 +48,7 @@ interface IInputMethodManager {
int softInputMode, boolean first, int windowFlags);
void showInputMethodPickerFromClient(in IInputMethodClient client);
+ void showInputMethodSubtypePickerFromClient(in IInputMethodClient client);
void setInputMethod(in IBinder token, String id);
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 9efc708..675760f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -61,18 +61,21 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.view.IWindowManager;
import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -93,6 +96,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final String TAG = "InputManagerService";
static final int MSG_SHOW_IM_PICKER = 1;
+ static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
static final int MSG_UNBIND_INPUT = 1000;
static final int MSG_BIND_INPUT = 1010;
@@ -109,6 +113,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final long TIME_TO_RECONNECT = 10*1000;
+ private static final int NOT_A_SUBTYPE_ID = -1;
+
final Context mContext;
final Handler mHandler;
final SettingsObserver mSettingsObserver;
@@ -224,6 +230,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String mCurId;
/**
+ * The current subtype of the current input method.
+ */
+ private InputMethodSubtype mCurrentSubtype;
+
+
+ /**
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
@@ -292,6 +304,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
AlertDialog mSwitchingDialog;
InputMethodInfo[] mIms;
CharSequence[] mItems;
+ int[] mSubtypeIds;
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
@@ -299,6 +312,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
}
@Override public void onChange(boolean selfChange) {
@@ -351,9 +366,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!doit) {
return true;
}
-
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, "");
+ resetSelectedInputMethodSubtype();
chooseNewDefaultIMELocked();
return true;
}
@@ -409,16 +424,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (!chooseNewDefaultIMELocked()) {
changed = true;
curIm = null;
- curInputMethodId = "";
Slog.i(TAG, "Unsetting current input method");
Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- curInputMethodId);
+ Settings.Secure.DEFAULT_INPUT_METHOD, "");
+ resetSelectedInputMethodSubtype();
}
}
}
}
-
+
if (curIm == null) {
// We currently don't have a default input method... is
// one now available?
@@ -509,6 +523,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (defIm != null) {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
+ putSelectedInputMethodSubtype(defIm, NOT_A_SUBTYPE_ID);
}
}
@@ -964,10 +979,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// sync, so we will never have a DEFAULT_INPUT_METHOD that is not
// enabled.
String id = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
+ Settings.Secure.DEFAULT_INPUT_METHOD);
if (id != null && id.length() > 0) {
try {
- setInputMethodLocked(id);
+ setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
mCurMethodId = null;
@@ -980,21 +995,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- void setInputMethodLocked(String id) {
+ /* package */ void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + id);
}
if (id.equals(mCurMethodId)) {
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ InputMethodSubtype subtype = info.getSubtypes().get(subtypeId);
+ if (subtype != mCurrentSubtype) {
+ synchronized (mMethodMap) {
+ if (mCurMethod != null) {
+ try {
+ putSelectedInputMethodSubtype(info, subtypeId);
+ mCurMethod.changeInputMethodSubtype(subtype);
+ } catch (RemoteException e) {
+ return;
+ }
+ }
+ }
+ }
+ }
return;
}
final long ident = Binder.clearCallingIdentity();
try {
mCurMethodId = id;
+ // Set a subtype to this input method.
+ // subtypeId the name of a subtype which will be set.
+ if (putSelectedInputMethodSubtype(info, subtypeId)) {
+ mCurrentSubtype = info.getSubtypes().get(subtypeId);
+ } else {
+ mCurrentSubtype = null;
+ }
+
Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD, id);
+ Settings.Secure.DEFAULT_INPUT_METHOD, id);
if (ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1226,7 +1264,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ public void showInputMethodSubtypePickerFromClient(IInputMethodClient client) {
+ synchronized (mMethodMap) {
+ if (mCurClient == null || client == null
+ || mCurClient.client.asBinder() != client.asBinder()) {
+ Slog.w(TAG, "Ignoring showInputSubtypeMethodDialogFromClient of: " + client);
+ }
+
+ mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER);
+ }
+ }
+
public void setInputMethod(IBinder token, String id) {
+ setInputMethodWithSubtype(token, id, NOT_A_SUBTYPE_ID);
+ }
+
+ private void setInputMethodWithSubtype(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -1243,7 +1296,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
long ident = Binder.clearCallingIdentity();
try {
- setInputMethodLocked(id);
+ setInputMethodLocked(id, subtypeId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1307,6 +1360,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
showInputMethodMenu();
return true;
+ case MSG_SHOW_IM_SUBTYPE_PICKER:
+ showInputMethodSubtypeMenu();
+ return true;
+
// ---------------------------------------------------------
case MSG_UNBIND_INPUT:
@@ -1417,9 +1474,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
break;
}
}
+ InputMethodInfo imi = enabled.get(i);
Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- enabled.get(i).getId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, imi.getId());
+ putSelectedInputMethodSubtype(imi, NOT_A_SUBTYPE_ID);
return true;
}
@@ -1490,7 +1548,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// ----------------------------------------------------------------------
- void showInputMethodMenu() {
+ private void showInputMethodMenu() {
+ showInputMethodMenuInternal(false);
+ }
+
+ private void showInputMethodSubtypeMenu() {
+ showInputMethodMenuInternal(true);
+ }
+
+ private void showInputMethodMenuInternal(boolean showSubtypes) {
if (DEBUG) Slog.v(TAG, "Show switching menu");
final Context context = mContext;
@@ -1499,42 +1565,78 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
String lastInputMethodId = Settings.Secure.getString(context
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
final List<InputMethodInfo> immis = getEnabledInputMethodList();
+ ArrayList<Integer> subtypeIds = new ArrayList<Integer>();
if (immis == null) {
return;
}
-
+
synchronized (mMethodMap) {
hideInputMethodMenuLocked();
int N = immis.size();
- final Map<CharSequence, InputMethodInfo> imMap =
- new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+ final Map<CharSequence, Pair<InputMethodInfo, Integer>> imMap =
+ new TreeMap<CharSequence, Pair<InputMethodInfo, Integer>>(Collator.getInstance());
for (int i = 0; i < N; ++i) {
InputMethodInfo property = immis.get(i);
if (property == null) {
continue;
}
- imMap.put(property.loadLabel(pm), property);
+ // TODO: Show only enabled subtypes
+ ArrayList<InputMethodSubtype> subtypes = property.getSubtypes();
+ CharSequence label = property.loadLabel(pm);
+ if (showSubtypes && subtypes.size() > 0) {
+ for (int j = 0; j < subtypes.size(); ++j) {
+ InputMethodSubtype subtype = subtypes.get(j);
+ CharSequence title;
+ int nameResId = subtype.getNameResId();
+ int modeResId = subtype.getModeResId();
+ if (nameResId != 0) {
+ title = pm.getText(property.getPackageName(), nameResId,
+ property.getServiceInfo().applicationInfo);
+ } else {
+ CharSequence language = subtype.getLocale();
+ CharSequence mode = modeResId == 0 ? null
+ : pm.getText(property.getPackageName(), modeResId,
+ property.getServiceInfo().applicationInfo);
+ // TODO: Use more friendly Title and UI
+ title = label + "," + (mode == null ? "" : mode) + ","
+ + (language == null ? "" : language);
+ }
+ imMap.put(title, new Pair<InputMethodInfo, Integer>(property, j));
+ }
+ } else {
+ imMap.put(label,
+ new Pair<InputMethodInfo, Integer>(property, NOT_A_SUBTYPE_ID));
+ subtypeIds.add(0);
+ }
}
N = imMap.size();
mItems = imMap.keySet().toArray(new CharSequence[N]);
- mIms = imMap.values().toArray(new InputMethodInfo[N]);
-
+ mIms = new InputMethodInfo[N];
+ mSubtypeIds = new int[N];
int checkedItem = 0;
for (int i = 0; i < N; ++i) {
+ Pair<InputMethodInfo, Integer> value = imMap.get(mItems[i]);
+ mIms[i] = value.first;
+ mSubtypeIds[i] = value.second;
if (mIms[i].getId().equals(lastInputMethodId)) {
- checkedItem = i;
- break;
+ int subtypeId = mSubtypeIds[i];
+ if ((subtypeId == NOT_A_SUBTYPE_ID)
+ || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+ || (subtypeId == lastInputMethodSubtypeId)) {
+ checkedItem = i;
+ }
}
}
-
+
AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideInputMethodMenu();
@@ -1559,13 +1661,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
synchronized (mMethodMap) {
- if (mIms == null || mIms.length <= which) {
+ if (mIms == null || mIms.length <= which
+ || mSubtypeIds == null || mSubtypeIds.length <= which) {
return;
}
InputMethodInfo im = mIms[which];
+ int subtypeId = mSubtypeIds[which];
hideInputMethodMenu();
if (im != null) {
- setInputMethodLocked(im.getId());
+ if ((subtypeId < 0)
+ || (subtypeId >= im.getSubtypes().size())) {
+ subtypeId = NOT_A_SUBTYPE_ID;
+ }
+ setInputMethodLocked(im.getId(), subtypeId);
}
}
}
@@ -1679,6 +1787,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD,
firstId != null ? firstId : "");
+ resetSelectedInputMethodSubtype();
}
// Previous state was enabled.
return true;
@@ -1698,6 +1807,53 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
+ private void resetSelectedInputMethodSubtype() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
+ }
+
+ private boolean putSelectedInputMethodSubtype(InputMethodInfo imi, int subtypeId) {
+ ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+ if (subtypeId >= 0 && subtypeId < subtypes.size()) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+ subtypes.get(subtypeId).hashCode());
+ return true;
+ } else {
+ resetSelectedInputMethodSubtype();
+ return false;
+ }
+ }
+
+ private int getSelectedInputMethodSubtypeId(String id) {
+ InputMethodInfo imi = mMethodMap.get(id);
+ if (imi == null) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+ int subtypeId;
+ try {
+ subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+ } catch (SettingNotFoundException e) {
+ return NOT_A_SUBTYPE_ID;
+ }
+ for (int i = 0; i < subtypes.size(); ++i) {
+ InputMethodSubtype ims = subtypes.get(i);
+ if (subtypeId == ims.hashCode()) {
+ return i;
+ }
+ }
+ return NOT_A_SUBTYPE_ID;
+ }
+
+ /**
+ * @return Return the current subtype of this input method.
+ */
+ public InputMethodSubtype getCurrentInputMethodSubtype() {
+ return mCurrentSubtype;
+ }
+
// ----------------------------------------------------------------------
@Override