diff options
| -rw-r--r-- | api/current.txt | 8 | ||||
| -rw-r--r-- | api/system-current.txt | 8 | ||||
| -rw-r--r-- | api/test-current.txt | 8 | ||||
| -rw-r--r-- | core/java/android/view/inputmethod/InputMethodSubtype.java | 57 | ||||
| -rw-r--r-- | core/java/android/view/textservice/SpellCheckerInfo.java | 2 | ||||
| -rw-r--r-- | core/java/android/view/textservice/SpellCheckerSubtype.java | 39 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 12 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 1 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java | 144 | ||||
| -rw-r--r-- | services/core/java/com/android/server/InputMethodManagerService.java | 6 |
10 files changed, 217 insertions, 68 deletions
diff --git a/api/current.txt b/api/current.txt index 9d9b2f5fa2ce..4bd0fc0574c1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -728,6 +728,7 @@ package android { field public static final int label = 16842753; // 0x1010001 field public static final int labelFor = 16843718; // 0x10103c6 field public static final int labelTextSize = 16843317; // 0x1010235 + field public static final int languageTag = 16844041; // 0x1010509 field public static final int largeHeap = 16843610; // 0x101035a field public static final int largeScreens = 16843398; // 0x1010286 field public static final int largestWidthLimitDp = 16843622; // 0x1010366 @@ -43213,7 +43214,8 @@ package android.view.inputmethod { method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); method public int getIconResId(); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public java.lang.String getMode(); method public int getNameResId(); method public boolean isAsciiCapable(); @@ -43228,6 +43230,7 @@ package android.view.inputmethod { method public android.view.inputmethod.InputMethodSubtype build(); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean); + method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int); @@ -43291,7 +43294,8 @@ package android.view.textservice { method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo); method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public int getNameResId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR; diff --git a/api/system-current.txt b/api/system-current.txt index f9c81374d648..18dd49fe1aaf 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -822,6 +822,7 @@ package android { field public static final int label = 16842753; // 0x1010001 field public static final int labelFor = 16843718; // 0x10103c6 field public static final int labelTextSize = 16843317; // 0x1010235 + field public static final int languageTag = 16844041; // 0x1010509 field public static final int largeHeap = 16843610; // 0x101035a field public static final int largeScreens = 16843398; // 0x1010286 field public static final int largestWidthLimitDp = 16843622; // 0x1010366 @@ -45554,7 +45555,8 @@ package android.view.inputmethod { method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); method public int getIconResId(); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public java.lang.String getMode(); method public int getNameResId(); method public boolean isAsciiCapable(); @@ -45569,6 +45571,7 @@ package android.view.inputmethod { method public android.view.inputmethod.InputMethodSubtype build(); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean); + method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int); @@ -45632,7 +45635,8 @@ package android.view.textservice { method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo); method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public int getNameResId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR; diff --git a/api/test-current.txt b/api/test-current.txt index a59d03dab47b..205b06df2027 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -728,6 +728,7 @@ package android { field public static final int label = 16842753; // 0x1010001 field public static final int labelFor = 16843718; // 0x10103c6 field public static final int labelTextSize = 16843317; // 0x1010235 + field public static final int languageTag = 16844041; // 0x1010509 field public static final int largeHeap = 16843610; // 0x101035a field public static final int largeScreens = 16843398; // 0x1010286 field public static final int largestWidthLimitDp = 16843622; // 0x1010366 @@ -43215,7 +43216,8 @@ package android.view.inputmethod { method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); method public int getIconResId(); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public java.lang.String getMode(); method public int getNameResId(); method public boolean isAsciiCapable(); @@ -43230,6 +43232,7 @@ package android.view.inputmethod { method public android.view.inputmethod.InputMethodSubtype build(); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean); + method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String); method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int); @@ -43293,7 +43296,8 @@ package android.view.textservice { method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo); method public java.lang.String getExtraValue(); method public java.lang.String getExtraValueOf(java.lang.String); - method public java.lang.String getLocale(); + method public java.lang.String getLanguageTag(); + method public deprecated java.lang.String getLocale(); method public int getNameResId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR; diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index fbaf51c27b61..a42f4d95c1ed 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -50,6 +51,7 @@ import java.util.Locale; * * @attr ref android.R.styleable#InputMethod_Subtype_label * @attr ref android.R.styleable#InputMethod_Subtype_icon + * @attr ref android.R.styleable#InputMethod_Subtype_languageTag * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue @@ -60,6 +62,7 @@ import java.util.Locale; */ public final class InputMethodSubtype implements Parcelable { private static final String TAG = InputMethodSubtype.class.getSimpleName(); + private static final String LANGUAGE_TAG_NONE = ""; private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "="; // TODO: remove this @@ -74,6 +77,7 @@ public final class InputMethodSubtype implements Parcelable { private final int mSubtypeNameResId; private final int mSubtypeId; private final String mSubtypeLocale; + private final String mSubtypeLanguageTag; private final String mSubtypeMode; private final String mSubtypeExtraValue; private volatile HashMap<String, String> mExtraValueHashMapCache; @@ -171,6 +175,15 @@ public final class InputMethodSubtype implements Parcelable { private String mSubtypeLocale = ""; /** + * @param languageTag is the BCP-47 Language Tag supported by this subtype. + */ + public InputMethodSubtypeBuilder setLanguageTag(String languageTag) { + mSubtypeLanguageTag = languageTag == null ? LANGUAGE_TAG_NONE : languageTag; + return this; + } + private String mSubtypeLanguageTag = LANGUAGE_TAG_NONE; + + /** * @param subtypeMode is the mode supported by this subtype. */ public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) { @@ -271,6 +284,7 @@ public final class InputMethodSubtype implements Parcelable { mSubtypeNameResId = builder.mSubtypeNameResId; mSubtypeIconResId = builder.mSubtypeIconResId; mSubtypeLocale = builder.mSubtypeLocale; + mSubtypeLanguageTag = builder.mSubtypeLanguageTag; mSubtypeMode = builder.mSubtypeMode; mSubtypeExtraValue = builder.mSubtypeExtraValue; mIsAuxiliary = builder.mIsAuxiliary; @@ -291,6 +305,8 @@ public final class InputMethodSubtype implements Parcelable { s = source.readString(); mSubtypeLocale = s != null ? s : ""; s = source.readString(); + mSubtypeLanguageTag = s != null ? s : LANGUAGE_TAG_NONE; + s = source.readString(); mSubtypeMode = s != null ? s : ""; s = source.readString(); mSubtypeExtraValue = s != null ? s : ""; @@ -318,21 +334,38 @@ public final class InputMethodSubtype implements Parcelable { /** * @return The locale of the subtype. This method returns the "locale" string parameter passed * to the constructor. + * + * @deprecated Use {@link #getLanguageTag()} instead. */ + @Deprecated + @NonNull public String getLocale() { return mSubtypeLocale; } /** - * @return The normalized {@link Locale} object of the subtype. The returned locale may or may - * not equal to "locale" string parameter passed to the constructor. + * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag + * is specified. * - * <p>TODO: Consider to make this a public API.</p> + * @see Locale#forLanguageTag(String) + */ + @NonNull + public String getLanguageTag() { + return mSubtypeLanguageTag; + } + + /** + * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not + * specified, then try to construct from {@link #getLocale()} + * + * <p>TODO: Consider to make this a public API, or move this to support lib.</p> * @hide */ @Nullable public Locale getLocaleObject() { - // TODO: Move the following method from InputMethodUtils to InputMethodSubtype. + if (!TextUtils.isEmpty(mSubtypeLanguageTag)) { + return Locale.forLanguageTag(mSubtypeLanguageTag); + } return InputMethodUtils.constructLocaleFromString(mSubtypeLocale); } @@ -476,13 +509,14 @@ public final class InputMethodSubtype implements Parcelable { return (subtype.hashCode() == hashCode()); } return (subtype.hashCode() == hashCode()) - && (subtype.getLocale().equals(getLocale())) - && (subtype.getMode().equals(getMode())) - && (subtype.getExtraValue().equals(getExtraValue())) - && (subtype.isAuxiliary() == isAuxiliary()) - && (subtype.overridesImplicitlyEnabledSubtype() - == overridesImplicitlyEnabledSubtype()) - && (subtype.isAsciiCapable() == isAsciiCapable()); + && (subtype.getLocale().equals(getLocale())) + && (subtype.getLanguageTag().equals(getLanguageTag())) + && (subtype.getMode().equals(getMode())) + && (subtype.getExtraValue().equals(getExtraValue())) + && (subtype.isAuxiliary() == isAuxiliary()) + && (subtype.overridesImplicitlyEnabledSubtype() + == overridesImplicitlyEnabledSubtype()) + && (subtype.isAsciiCapable() == isAsciiCapable()); } return false; } @@ -497,6 +531,7 @@ public final class InputMethodSubtype implements Parcelable { dest.writeInt(mSubtypeNameResId); dest.writeInt(mSubtypeIconResId); dest.writeString(mSubtypeLocale); + dest.writeString(mSubtypeLanguageTag); dest.writeString(mSubtypeMode); dest.writeString(mSubtypeExtraValue); dest.writeInt(mIsAuxiliary ? 1 : 0); diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java index 491de781a20d..471b6d4322f7 100644 --- a/core/java/android/view/textservice/SpellCheckerInfo.java +++ b/core/java/android/view/textservice/SpellCheckerInfo.java @@ -117,6 +117,8 @@ public final class SpellCheckerInfo implements Parcelable { a.getString(com.android.internal.R.styleable .SpellChecker_Subtype_subtypeLocale), a.getString(com.android.internal.R.styleable + .SpellChecker_Subtype_languageTag), + a.getString(com.android.internal.R.styleable .SpellChecker_Subtype_subtypeExtraValue), a.getInt(com.android.internal.R.styleable .SpellChecker_Subtype_subtypeId, 0)); diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java index f2b03ccaab8c..df336988cf8f 100644 --- a/core/java/android/view/textservice/SpellCheckerSubtype.java +++ b/core/java/android/view/textservice/SpellCheckerSubtype.java @@ -18,6 +18,7 @@ package android.view.textservice; import com.android.internal.inputmethod.InputMethodUtils; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -40,6 +41,7 @@ import java.util.Locale; * @see SpellCheckerInfo * * @attr ref android.R.styleable#SpellChecker_Subtype_label + * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeId @@ -49,11 +51,13 @@ public final class SpellCheckerSubtype implements Parcelable { private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "="; private static final int SUBTYPE_ID_NONE = 0; + private static final String SUBTYPE_LANGUAGE_TAG_NONE = ""; private final int mSubtypeId; private final int mSubtypeHashCode; private final int mSubtypeNameResId; private final String mSubtypeLocale; + private final String mSubtypeLanguageTag; private final String mSubtypeExtraValue; private HashMap<String, String> mExtraValueHashMapCache; @@ -66,14 +70,17 @@ public final class SpellCheckerSubtype implements Parcelable { * * @param nameId The name of the subtype * @param locale The locale supported by the subtype + * @param languageTag The BCP-47 Language Tag associated with this subtype. * @param extraValue The extra value of the subtype * @param subtypeId The subtype ID that is supposed to be stable during package update. * * @hide */ - public SpellCheckerSubtype(int nameId, String locale, String extraValue, int subtypeId) { + public SpellCheckerSubtype(int nameId, String locale, String languageTag, String extraValue, + int subtypeId) { mSubtypeNameResId = nameId; mSubtypeLocale = locale != null ? locale : ""; + mSubtypeLanguageTag = languageTag != null ? languageTag : SUBTYPE_LANGUAGE_TAG_NONE; mSubtypeExtraValue = extraValue != null ? extraValue : ""; mSubtypeId = subtypeId; mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ? @@ -91,7 +98,7 @@ public final class SpellCheckerSubtype implements Parcelable { * to instantiate {@link SpellCheckerSubtype} object. */ public SpellCheckerSubtype(int nameId, String locale, String extraValue) { - this(nameId, locale, extraValue, SUBTYPE_ID_NONE); + this(nameId, locale, SUBTYPE_LANGUAGE_TAG_NONE, extraValue, SUBTYPE_ID_NONE); } SpellCheckerSubtype(Parcel source) { @@ -100,6 +107,8 @@ public final class SpellCheckerSubtype implements Parcelable { s = source.readString(); mSubtypeLocale = s != null ? s : ""; s = source.readString(); + mSubtypeLanguageTag = s != null ? s : ""; + s = source.readString(); mSubtypeExtraValue = s != null ? s : ""; mSubtypeId = source.readInt(); mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ? @@ -115,12 +124,27 @@ public final class SpellCheckerSubtype implements Parcelable { /** * @return the locale of the subtype + * + * @deprecated Use {@link #getLanguageTag()} instead. */ + @Deprecated + @NonNull public String getLocale() { return mSubtypeLocale; } /** + * @return the BCP-47 Language Tag of the subtype. Returns an empty string when no Language Tag + * is specified. + * + * @see Locale#forLanguageTag(String) + */ + @NonNull + public String getLanguageTag() { + return mSubtypeLanguageTag; + } + + /** * @return the extra value of the subtype */ public String getExtraValue() { @@ -182,20 +206,24 @@ public final class SpellCheckerSubtype implements Parcelable { return (subtype.hashCode() == hashCode()) && (subtype.getNameResId() == getNameResId()) && (subtype.getLocale().equals(getLocale())) + && (subtype.getLanguageTag().equals(getLanguageTag())) && (subtype.getExtraValue().equals(getExtraValue())); } return false; } /** - * @return The normalized {@link Locale} object of the subtype. The returned locale may or may - * not equal to "locale" string parameter passed to the constructor. + * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not + * specified, then try to construct from {@link #getLocale()} * - * <p>TODO: Consider to make this a public API.</p> + * <p>TODO: Consider to make this a public API, or move this to support lib.</p> * @hide */ @Nullable public Locale getLocaleObject() { + if (!TextUtils.isEmpty(mSubtypeLanguageTag)) { + return Locale.forLanguageTag(mSubtypeLanguageTag); + } return InputMethodUtils.constructLocaleFromString(mSubtypeLocale); } @@ -234,6 +262,7 @@ public final class SpellCheckerSubtype implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(mSubtypeNameResId); dest.writeString(mSubtypeLocale); + dest.writeString(mSubtypeLanguageTag); dest.writeString(mSubtypeExtraValue); dest.writeInt(mSubtypeId); } diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 9bca3d691506..786554cc41ba 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3042,7 +3042,8 @@ i <attr name="imeSubtypeLocale" format="string" /> <!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this string will be passed to the IME when the framework calls the IME with the - subtype. --> + subtype. {@link android.view.inputmethod.InputMethodSubtype#getLocale()} returns the + value specified in this attribute. --> <attr name="imeSubtypeMode" format="string" /> <!-- Set true if the subtype is auxiliary. An auxiliary subtype won't be shown in the input method selection list in the settings app. @@ -3067,6 +3068,9 @@ i this subtype. This is important because many password fields only allow ASCII-characters. --> <attr name="isAsciiCapable" format="boolean" /> + <!-- The BCP-47 Language Tag of the subtype. This replaces + {@link android.R.styleable#InputMethod_Subtype_imeSubtypeLocale}. --> + <attr name="languageTag" format="string" /> </declare-styleable> <!-- Use <code>spell-checker</code> as the root tag of the XML resource that @@ -3090,7 +3094,8 @@ i <attr name="label" /> <!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...) This is also used by the framework to know the supported locales - of the spell checker. --> + of the spell checker. {@link android.view.textservice.SpellCheckerSubtype#getLocale()} + returns the value specified in this attribute. --> <attr name="subtypeLocale" format="string" /> <!-- The extra value of the subtype. This string can be any string and will be passed to the SpellChecker. --> @@ -3102,6 +3107,9 @@ i {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue}) will be used instead. --> <attr name="subtypeId" /> + <!-- The BCP-47 Language Tag of the subtype. This replaces + {@link android.R.styleable#SpellChecker_Subtype_subtypeLocale}. --> + <attr name="languageTag" /> </declare-styleable> <!-- Use <code>accessibility-service</code> as the root tag of the XML resource that diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b6b2e204b9cf..addeb05f86f2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2683,6 +2683,7 @@ <public type="attr" name="encryptionAware" /> <public type="attr" name="preferenceFragmentStyle" /> <public type="attr" name="canControlMagnification" /> + <public type="attr" name="languageTag" /> <public type="style" name="Theme.Material.DayNight" /> <public type="style" name="Theme.Material.DayNight.DarkActionBar" /> diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java index 73fdb103fce1..4a1c414234bd 100644 --- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java +++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java @@ -30,11 +30,16 @@ import static android.test.MoreAsserts.assertNotEqual; */ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { private static final int SUBTYPE_SUBTYPE_ID_NONE = 0; + private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_NONE = ""; + private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE = ""; + private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_A = "en_GB"; + private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_A = "en-GB"; private static final int SUBTYPE_NAME_RES_ID_A = 0x12345; private static final String SUBTYPE_EXTRA_VALUE_A = "Key1=Value1,Key2=Value2"; private static final int SUBTYPE_SUBTYPE_ID_A = 42; private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_B = "en_IN"; + private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_B = "en-IN"; private static final int SUBTYPE_NAME_RES_ID_B = 0x54321; private static final String SUBTYPE_EXTRA_VALUE_B = "Key3=Value3,Key4=Value4"; private static final int SUBTYPE_SUBTYPE_ID_B = -42; @@ -60,9 +65,11 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { @SmallTest public void testSubtypeWithNoSubtypeId() throws Exception { final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, - SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE); + SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, + SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE); assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId()); assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale()); + assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag()); assertEquals("Value1", subtype.getExtraValueOf("Key1")); assertEquals("Value2", subtype.getExtraValueOf("Key2")); // Historically we have used SpellCheckerSubtype#hashCode() to track which subtype is @@ -75,6 +82,7 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype); assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId()); assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale()); + assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag()); assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1")); assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2")); assertEquals( @@ -84,10 +92,12 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { public void testSubtypeWithSubtypeId() throws Exception { final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, - SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A); + SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, + SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A); assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId()); assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale()); + assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag()); assertEquals("Value1", subtype.getExtraValueOf("Key1")); assertEquals("Value2", subtype.getExtraValueOf("Key2")); // Similar to "SubtypeId" in InputMethodSubtype, "SubtypeId" in SpellCheckerSubtype enables @@ -97,6 +107,7 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype); assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId()); assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale()); + assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag()); assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1")); assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2")); assertEquals(SUBTYPE_SUBTYPE_ID_A, clonedSubtype.hashCode()); @@ -104,30 +115,42 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { @SmallTest public void testGetLocaleObject() throws Exception { - assertEquals(new Locale("en"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "en", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("en", "US"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "en_US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("en", "US", "POSIX"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "en_US_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - - // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not - // support three letter language codes, and used "tl" (Tagalog) as the language string for - // "fil" (Filipino). - assertEquals(new Locale("fil"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "tl", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("fil", "PH"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "tl_PH", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("fil", "PH", "POSIX"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "tl_PH_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - - // So far rejecting invalid/unexpected locale strings is out of the scope. - assertEquals(new Locale("a"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "a", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("a b c"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "a b c", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); - assertEquals(new Locale("en-US"), new SpellCheckerSubtype( - SUBTYPE_NAME_RES_ID_A, "en-US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject()); + assertEquals(new Locale("en", "GB"), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_GB", + SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); + assertEquals(new Locale("en", "GB"), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, + "en-GB", SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); + + // If neither locale string nor language tag is specified, + // {@link SpellCheckerSubtype#getLocaleObject} returns null. + assertNull( + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, + SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); + + // If both locale string and language tag are specified, + // {@link SpellCheckerSubtype#getLocaleObject} uses language tag. + assertEquals(new Locale("en", "GB"), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_US", "en-GB", + SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); + + // Make sure that "tl_PH" is rewritten to "fil_PH" for spell checkers that need to support + // Android KitKat and prior, which do not support 3-letter language codes. + assertEquals(new Locale("fil", "PH"), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "tl_PH", + SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject()); + + // "languageTag" attribute is available in Android N and later, where 3-letter country codes + // are guaranteed to be available. It's developers' responsibility for specifying a valid + // country subtags here and we do not rewrite "tl" to "fil" for simplicity. + assertEquals("tl", + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE, + "tl-PH", SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE) + .getLocaleObject().getLanguage()); } @SmallTest @@ -156,50 +179,83 @@ public class SpellCheckerSubtypeTest extends InstrumentationTestCase { // If subtype ID is 0 (== SUBTYPE_SUBTYPE_ID_NONE), we keep the same behavior. assertEquals( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE)); assertNotEqual( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE)); assertNotEqual( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE)); + assertNotEqual( + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, + SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE)); assertNotEqual( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_NONE), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_NONE)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B, + SUBTYPE_SUBTYPE_ID_NONE)); // If subtype ID is not 0, we test the equality based only on the subtype ID. assertEquals( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A)); assertEquals( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A)); assertEquals( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A)); + assertEquals( + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), + new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, + SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A)); assertEquals( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_A)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B, + SUBTYPE_SUBTYPE_ID_A)); assertNotEqual( new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A), + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_A), new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A, - SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_B)); + SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A, + SUBTYPE_SUBTYPE_ID_B)); } + } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 45c1ed224d49..0282a72784c8 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -3536,6 +3536,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String ATTR_LABEL = "label"; private static final String ATTR_ICON = "icon"; private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale"; + private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag"; private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode"; private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue"; private static final String ATTR_IS_AUXILIARY = "isAuxiliary"; @@ -3629,6 +3630,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId())); out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId())); out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale()); + out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG, + subtype.getLanguageTag()); out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode()); out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue()); out.attribute(null, ATTR_IS_AUXILIARY, @@ -3690,6 +3693,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub parser.getAttributeValue(null, ATTR_LABEL)); final String imeSubtypeLocale = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE); + final String languageTag = + parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG); final String imeSubtypeMode = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE); final String imeSubtypeExtraValue = @@ -3700,6 +3705,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub .setSubtypeNameResId(label) .setSubtypeIconResId(icon) .setSubtypeLocale(imeSubtypeLocale) + .setLanguageTag(languageTag) .setSubtypeMode(imeSubtypeMode) .setSubtypeExtraValue(imeSubtypeExtraValue) .setIsAuxiliary(isAuxiliary) |