diff options
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=""selected_input_method_subtype"" + 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<android.view.inputmethod.InputMethodInfo.InputMethodSubtype>" + return="java.util.ArrayList<android.view.inputmethod.InputMethodSubtype>" 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 |