From 238faad984786f0227fe6c0cf6ea2a5ecff3a4bc Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Fri, 11 Mar 2016 10:39:51 -0800 Subject: [PATCH] Use LocaleUtils#filterByLanguage for non-keyboard subtypes. With this CL, we expand the target of Bug 27129703 and Bug 27348943 to non-keyboard subtypes. Suppose there is a handwriting IME (mode == "handwriting") that supports the following 5 subtypes. - en-US - en-GB - fr - sr-Cyrl - sr-Latn Also suppose the system languages are configured as follows. 1. sr-Latn-RS 2. ja-JP 3. fr-FR 4. en-GB 5. en-US In this case we want to enable [sr-Latn, fr, en-GB] by default when "use system language" is checked in the subtype enabler. See previous commits [1][2] about how we addressed those issues for keyboard subtypes. [1]: Iaf179d60c12b9a98b4f097e2449471c4184e049b e985c240e3feb62ea38d5b4e386be083ca0f215b [2]: I8fc774154f5175abff2f16e8f12a4847bf5f5b7c 072a95a3094af2ced4f009ad62c4553c28e3f830 Bug: 27560993 Change-Id: I416b5671602c550805ed6267dd210968aa1de83c --- .../inputmethod/InputMethodUtils.java | 29 +++-- .../inputmethod/InputMethodUtilsTest.java | 104 ++++++++++++++++++ 2 files changed, 118 insertions(+), 15 deletions(-) diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index 7a3c253a8544e..90ee05ec944b3 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -505,7 +505,6 @@ public class InputMethodUtils { final int numSubtypes = subtypes.size(); // Handle overridesImplicitlyEnabledSubtype mechanism. - final String systemLanguage = systemLocales.get(0).getLanguage(); final HashMap applicableModeAndSubtypesMap = new HashMap<>(); for (int i = 0; i < numSubtypes; ++i) { // scan overriding implicitly enabled subtypes. @@ -521,25 +520,20 @@ public class InputMethodUtils { return new ArrayList<>(applicableModeAndSubtypesMap.values()); } + final HashMap> nonKeyboardSubtypesMap = + new HashMap<>(); final ArrayList keyboardSubtypes = new ArrayList<>(); + for (int i = 0; i < numSubtypes; ++i) { final InputMethodSubtype subtype = subtypes.get(i); - if (TextUtils.equals(SUBTYPE_MODE_KEYBOARD, subtype.getMode())) { + final String mode = subtype.getMode(); + if (SUBTYPE_MODE_KEYBOARD.equals(mode)) { keyboardSubtypes.add(subtype); } else { - final Locale locale = subtype.getLocaleObject(); - final String mode = subtype.getMode(); - // TODO: Use LocaleUtils#filterByLanguage() instead. - if (locale != null && TextUtils.equals(locale.getLanguage(), systemLanguage)) { - final InputMethodSubtype applicableSubtype = - applicableModeAndSubtypesMap.get(mode); - // If more applicable subtypes are contained, skip. - if (applicableSubtype != null) { - if (systemLocale.equals(applicableSubtype.getLocaleObject())) continue; - if (!systemLocale.equals(locale)) continue; - } - applicableModeAndSubtypesMap.put(mode, subtype); + if (!nonKeyboardSubtypesMap.containsKey(mode)) { + nonKeyboardSubtypesMap.put(mode, new ArrayList<>()); } + nonKeyboardSubtypesMap.get(mode).add(subtype); } } @@ -578,7 +572,12 @@ public class InputMethodUtils { } } - applicableSubtypes.addAll(applicableModeAndSubtypesMap.values()); + // For each non-keyboard mode, extract subtypes with system locales. + for (final ArrayList subtypeList : nonKeyboardSubtypesMap.values()) { + LocaleUtils.filterByLanguage(subtypeList, sSubtypeToLocale, systemLocales, + applicableSubtypes); + } + return applicableSubtypes; } diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java index 20e9165aa1b4f..6b5e4adf90103 100644 --- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java @@ -219,12 +219,28 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); + final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr", + "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, + !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); + final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ", + "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX, + !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, + !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr", SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); + final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr", + "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX, + !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, + !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); + final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ", + "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX, + !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, + !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype = createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, @@ -430,6 +446,85 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { verifyEquality(nonAutoEnUS, result.get(0)); } + // Make sure that both language and script are taken into account to find the best matching + // subtype. + { + final ArrayList subtypes = new ArrayList<>(); + subtypes.add(nonAutoEnUS); + subtypes.add(nonAutoSrCyrl); + subtypes.add(nonAutoSrLatn); + subtypes.add(nonAutoHandwritingEn); + subtypes.add(nonAutoHandwritingFr); + subtypes.add(nonAutoHandwritingSrCyrl); + subtypes.add(nonAutoHandwritingSrLatn); + final InputMethodInfo imi = createDummyInputMethodInfo( + "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + subtypes); + final ArrayList result = + InputMethodUtils.getImplicitlyApplicableSubtypesLocked( + getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi); + assertEquals(2, result.size()); + assertThat(nonAutoSrLatn, isIn(result)); + assertThat(nonAutoHandwritingSrLatn, isIn(result)); + } + { + final ArrayList subtypes = new ArrayList<>(); + subtypes.add(nonAutoEnUS); + subtypes.add(nonAutoSrCyrl); + subtypes.add(nonAutoSrLatn); + subtypes.add(nonAutoHandwritingEn); + subtypes.add(nonAutoHandwritingFr); + subtypes.add(nonAutoHandwritingSrCyrl); + subtypes.add(nonAutoHandwritingSrLatn); + final InputMethodInfo imi = createDummyInputMethodInfo( + "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + subtypes); + final ArrayList result = + InputMethodUtils.getImplicitlyApplicableSubtypesLocked( + getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi); + assertEquals(2, result.size()); + assertThat(nonAutoSrCyrl, isIn(result)); + assertThat(nonAutoHandwritingSrCyrl, isIn(result)); + } + + // Make sure that secondary locales are taken into account to find the best matching + // subtype. + { + final ArrayList subtypes = new ArrayList<>(); + subtypes.add(nonAutoEnUS); + subtypes.add(nonAutoEnGB); + subtypes.add(nonAutoSrCyrl); + subtypes.add(nonAutoSrLatn); + subtypes.add(nonAutoFr); + subtypes.add(nonAutoFrCA); + subtypes.add(nonAutoHandwritingEn); + subtypes.add(nonAutoHandwritingFr); + subtypes.add(nonAutoHandwritingSrCyrl); + subtypes.add(nonAutoHandwritingSrLatn); + final InputMethodInfo imi = createDummyInputMethodInfo( + "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + subtypes); + final ArrayList result = + InputMethodUtils.getImplicitlyApplicableSubtypesLocked( + getResourcesForLocales( + Locale.forLanguageTag("sr-Latn-RS-x-android"), + Locale.forLanguageTag("ja-JP"), + Locale.forLanguageTag("fr-FR"), + Locale.forLanguageTag("en-GB"), + Locale.forLanguageTag("en-US")), + imi); + assertEquals(6, result.size()); + assertThat(nonAutoEnGB, isIn(result)); + assertThat(nonAutoFr, isIn(result)); + assertThat(nonAutoSrLatn, isIn(result)); + assertThat(nonAutoHandwritingEn, isIn(result)); + assertThat(nonAutoHandwritingFr, isIn(result)); + assertThat(nonAutoHandwritingSrLatn, isIn(result)); + } + // Make sure that 3-letter language code can be handled. { final ArrayList subtypes = new ArrayList<>(); @@ -778,7 +873,15 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) { + return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary, + overridesImplicitlyEnabledSubtype, isAsciiCapable, + isEnabledWhenDefaultIsNotAsciiCapable); + } + private static InputMethodSubtype createDummyInputMethodSubtype(String locale, + String languageTag, String mode, boolean isAuxiliary, + boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, + boolean isEnabledWhenDefaultIsNotAsciiCapable) { final StringBuilder subtypeExtraValue = new StringBuilder(); if (isEnabledWhenDefaultIsNotAsciiCapable) { subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR); @@ -796,6 +899,7 @@ public class InputMethodUtilsTest extends InstrumentationTestCase { .setSubtypeNameResId(0) .setSubtypeIconResId(0) .setSubtypeLocale(locale) + .setLanguageTag(languageTag) .setSubtypeMode(mode) .setSubtypeExtraValue(subtypeExtraValue.toString()) .setIsAuxiliary(isAuxiliary)