summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2014-11-01 21:04:30 +0900
committerYohei Yukawa <yukawa@google.com>2014-11-06 15:28:06 +0900
commitb21220efae92a56ff7b4b781fa614a6e3a8a3007 (patch)
tree60bfb9473ff210c79cb6569adae534cef48020f0
parent8b26674aa139965005ff3ba7d284d7d557c2de61 (diff)
downloadframeworks_base-b21220efae92a56ff7b4b781fa614a6e3a8a3007.zip
frameworks_base-b21220efae92a56ff7b4b781fa614a6e3a8a3007.tar.gz
frameworks_base-b21220efae92a56ff7b4b781fa614a6e3a8a3007.tar.bz2
Minimize the number of default enabled IMEs part 4
This is a follow up CL for recent attempt to minimize the number of default enabled IMEs. - part1: I831502db502f4073c9c2f50ce7705a4e45e2e1e3 - part2: Ife93d909fb8a24471c425c903e2b7048826e17a3 - part3: I6571d464a46453934f0a8f5e79018a67a9a3c845 It turned out that the changes made in part2 and part3 are a bit overkill, and users will see no software keyboards in some particular situations. The problem we missed in the previous CLs is the fact that InputMethodInfo#isDefault is indeed a locale-dependent value, hence it may vary depending on the system locale. Existing unittests also failed to abstract such locale-dependent nature. In order to addresses that regression, the selection logic is a bit widely reorganized in this CL. Now the logic is implemented as a series of fallback rules. Also, unit tests are updated to be able to 1) test the order of the enabled IMEs, and 2) emulate the locale-dependent behavior of InputMethodInfo#isDefault to enrich test cases. BUG: 17347871 BUG: 18192576 Change-Id: I871ccda787eb0f1099ba3574356c1da4b33681f3
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java310
-rw-r--r--core/tests/inputmethodtests/src/android/os/InputMethodTest.java181
2 files changed, 325 insertions, 166 deletions
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index ac915d1..183527c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.inputmethod;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -34,7 +36,9 @@ import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.TextServicesManager;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -115,8 +119,8 @@ public class InputMethodUtils {
}
/**
- * @deprecated Use {@link Locale} returned from
- * {@link #getFallbackLocaleForDefaultIme(ArrayList)} instead.
+ * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean,
+ * Locale, boolean, String)} instead.
*/
@Deprecated
public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) {
@@ -126,25 +130,60 @@ public class InputMethodUtils {
return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD);
}
+ private static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi,
+ final Context context, final boolean checkDefaultAttribute,
+ @Nullable final Locale requiredLocale, final boolean checkCountry,
+ final String requiredSubtypeMode) {
+ if (!isSystemIme(imi)) {
+ return false;
+ }
+ if (checkDefaultAttribute && !imi.isDefault(context)) {
+ return false;
+ }
+ if (!containsSubtypeOf(imi, requiredLocale, checkCountry, requiredSubtypeMode)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Nullable
public static Locale getFallbackLocaleForDefaultIme(final ArrayList<InputMethodInfo> imis,
final Context context) {
+ // At first, find the fallback locale from the IMEs that are declared as "default" in the
+ // current locale. Note that IME developers can declare an IME as "default" only for
+ // some particular locales but "not default" for other locales.
for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) {
for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (isSystemIme(imi) && imi.isDefault(context) &&
- containsSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
+ if (isSystemImeThatHasSubtypeOf(imis.get(i), context,
+ true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) {
+ return fallbackLocale;
+ }
+ }
+ }
+ // If no fallback locale is found in the above condition, find fallback locales regardless
+ // of the "default" attribute as a last resort.
+ for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) {
+ for (int i = 0; i < imis.size(); ++i) {
+ if (isSystemImeThatHasSubtypeOf(imis.get(i), context,
+ false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) {
return fallbackLocale;
}
}
}
+ Slog.w(TAG, "Found no fallback locale. imis=" + Arrays.toString(imis.toArray()));
return null;
}
- private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(InputMethodInfo imi) {
+ private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(final InputMethodInfo imi,
+ final Context context, final boolean checkDefaultAttribute) {
if (!isSystemIme(imi)) {
return false;
}
+ if (checkDefaultAttribute && !imi.isDefault(context)) {
+ return false;
+ }
if (!imi.isAuxiliaryIme()) {
return false;
}
@@ -166,98 +205,184 @@ public class InputMethodUtils {
}
}
- public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
- Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) {
- // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant.
- final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
+ private static final class InputMethodListBuilder {
+ // Note: We use LinkedHashSet instead of android.util.ArraySet because the enumeration
+ // order can have non-trivial effect in the call sites.
+ @NonNull
+ private final LinkedHashSet<InputMethodInfo> mInputMethodSet = new LinkedHashSet<>();
- if (!isSystemReady) {
- final ArrayList<InputMethodInfo> retval = new ArrayList<>();
+ public InputMethodListBuilder fillImes(final ArrayList<InputMethodInfo> imis,
+ final Context context, final boolean checkDefaultAttribute,
+ @Nullable final Locale locale, final boolean checkCountry,
+ final String requiredSubtypeMode) {
for (int i = 0; i < imis.size(); ++i) {
final InputMethodInfo imi = imis.get(i);
- // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
- if (isSystemIme(imi) && imi.isDefault(context) &&
- isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
- retval.add(imi);
+ if (isSystemImeThatHasSubtypeOf(imi, context, checkDefaultAttribute, locale,
+ checkCountry, requiredSubtypeMode)) {
+ mInputMethodSet.add(imi);
}
}
- return retval;
+ return this;
}
- // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant.
- final Locale systemLocale = getSystemLocaleFromContext(context);
- // TODO: Use LinkedHashSet to simplify the code.
- final ArrayList<InputMethodInfo> retval = new ArrayList<>();
- boolean systemLocaleKeyboardImeFound = false;
-
- // First, try to find IMEs with taking the system locale country into consideration.
- for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (!isSystemIme(imi) || !imi.isDefault(context)) {
- continue;
- }
- final boolean isSystemLocaleKeyboardIme = isImeThatHasSubtypeOf(imi, systemLocale,
- false /* ignoreCountry */, SUBTYPE_MODE_KEYBOARD);
- // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
- // TODO: Use LinkedHashSet to simplify the code.
- if (isSystemLocaleKeyboardIme ||
- isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_ANY)) {
- retval.add(imi);
+ // TODO: The behavior of InputMethodSubtype#overridesImplicitlyEnabledSubtype() should be
+ // documented more clearly.
+ public InputMethodListBuilder fillAuxiliaryImes(final ArrayList<InputMethodInfo> imis,
+ final Context context) {
+ // If one or more auxiliary input methods are available, OK to stop populating the list.
+ for (final InputMethodInfo imi : mInputMethodSet) {
+ if (imi.isAuxiliaryIme()) {
+ return this;
+ }
}
- systemLocaleKeyboardImeFound |= isSystemLocaleKeyboardIme;
- }
-
- // System locale country doesn't match any IMEs, try to find IMEs in a country-agnostic
- // way.
- if (!systemLocaleKeyboardImeFound) {
+ boolean added = false;
for (int i = 0; i < imis.size(); ++i) {
final InputMethodInfo imi = imis.get(i);
- if (!isSystemIme(imi) || !imi.isDefault(context)) {
- continue;
- }
- if (isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
- // IMEs that have fallback locale are already added in the previous loop. We
- // don't need to add them again here.
- // TODO: Use LinkedHashSet to simplify the code.
- continue;
+ if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context,
+ true /* checkDefaultAttribute */)) {
+ mInputMethodSet.add(imi);
+ added = true;
}
- if (isImeThatHasSubtypeOf(imi, systemLocale, true /* ignoreCountry */,
- SUBTYPE_MODE_ANY)) {
- retval.add(imi);
+ }
+ if (added) {
+ return this;
+ }
+ for (int i = 0; i < imis.size(); ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context,
+ false /* checkDefaultAttribute */)) {
+ mInputMethodSet.add(imi);
}
}
+ return this;
}
- // If one or more auxiliary input methods are available, OK to stop populating the list.
- for (int i = 0; i < retval.size(); ++i) {
- if (retval.get(i).isAuxiliaryIme()) {
- return retval;
- }
+ public boolean isEmpty() {
+ return mInputMethodSet.isEmpty();
}
- for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi)) {
- retval.add(imi);
- }
+
+ @NonNull
+ public ArrayList<InputMethodInfo> build() {
+ return new ArrayList<>(mInputMethodSet);
}
- return retval;
}
- public static boolean isImeThatHasSubtypeOf(final InputMethodInfo imi,
- final Locale locale, final boolean ignoreCountry, final String mode) {
- if (locale == null) {
- return false;
- }
- return containsSubtypeOf(imi, locale, ignoreCountry, mode);
+ private static InputMethodListBuilder getMinimumKeyboardSetWithoutSystemLocale(
+ final ArrayList<InputMethodInfo> imis, final Context context,
+ @Nullable final Locale fallbackLocale) {
+ // Before the system becomes ready, we pick up at least one keyboard in the following order.
+ // The first user (device owner) falls into this category.
+ // 1. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true
+ // 2. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true
+ // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false
+ // 4. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false
+ // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
+
+ final InputMethodListBuilder builder = new InputMethodListBuilder();
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray())
+ + " fallbackLocale=" + fallbackLocale);
+ return builder;
+ }
+
+ private static InputMethodListBuilder getMinimumKeyboardSetWithSystemLocale(
+ final ArrayList<InputMethodInfo> imis, final Context context,
+ @Nullable final Locale systemLocale, @Nullable final Locale fallbackLocale) {
+ // Once the system becomes ready, we pick up at least one keyboard in the following order.
+ // Secondary users fall into this category in general.
+ // 1. checkDefaultAttribute: true, locale: systemLocale, checkCountry: true
+ // 2. checkDefaultAttribute: true, locale: systemLocale, checkCountry: false
+ // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true
+ // 4. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false
+ // 5. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true
+ // 6. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false
+ // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
+
+ final InputMethodListBuilder builder = new InputMethodListBuilder();
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray())
+ + " systemLocale=" + systemLocale + " fallbackLocale=" + fallbackLocale);
+ return builder;
+ }
+
+ public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context,
+ final boolean isSystemReady, final ArrayList<InputMethodInfo> imis) {
+ final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
+ if (!isSystemReady) {
+ // When the system is not ready, the system locale is not stable and reliable. Hence
+ // we will pick up IMEs that support software keyboard based on the fallback locale.
+ // Also pick up suitable IMEs regardless of the software keyboard support.
+ // (e.g. Voice IMEs)
+ return getMinimumKeyboardSetWithoutSystemLocale(imis, context, fallbackLocale)
+ .fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_ANY)
+ .build();
+ }
+
+ // When the system is ready, we will primarily rely on the system locale, but also keep
+ // relying on the fallback locale as a last resort.
+ // Also pick up suitable IMEs regardless of the software keyboard support (e.g. Voice IMEs),
+ // then pick up suitable auxiliary IMEs when necessary (e.g. Voice IMEs with "automatic"
+ // subtype)
+ final Locale systemLocale = getSystemLocaleFromContext(context);
+ return getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale)
+ .fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ true /* checkCountry */, SUBTYPE_MODE_ANY)
+ .fillAuxiliaryImes(imis, context)
+ .build();
}
/**
- * @deprecated Use {@link #isSystemIme(InputMethodInfo)} and
- * {@link InputMethodInfo#isDefault(Context)} and
- * {@link #isImeThatHasSubtypeOf(InputMethodInfo, Locale, boolean, String))} instead.
+ * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean,
+ * Locale, boolean, String)} instead.
*/
@Deprecated
public static boolean isValidSystemDefaultIme(
@@ -285,22 +410,25 @@ public class InputMethodUtils {
}
public static boolean containsSubtypeOf(final InputMethodInfo imi,
- final Locale locale, final boolean ignoreCountry, final String mode) {
+ @Nullable final Locale locale, final boolean checkCountry, final String mode) {
+ if (locale == null) {
+ return false;
+ }
final int N = imi.getSubtypeCount();
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- if (ignoreCountry) {
- final Locale subtypeLocale = new Locale(getLanguageFromLocaleString(
- subtype.getLocale()));
- if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) {
- continue;
- }
- } else {
+ if (checkCountry) {
// TODO: Use {@link Locale#toLanguageTag()} and
// {@link Locale#forLanguageTag(languageTag)} instead.
if (!TextUtils.equals(subtype.getLocale(), locale.toString())) {
continue;
}
+ } else {
+ final Locale subtypeLocale = new Locale(getLanguageFromLocaleString(
+ subtype.getLocale()));
+ if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) {
+ continue;
+ }
}
if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode) ||
mode.equalsIgnoreCase(subtype.getMode())) {
@@ -465,19 +593,9 @@ public class InputMethodUtils {
return applicableSubtypes;
}
- private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
- Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes,
- boolean allowsImplicitlySelectedSubtypes) {
- if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) {
- enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
- context.getResources(), imi);
- }
- return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes);
- }
-
/**
* Returns the language component of a given locale string.
- * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}
+ * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(String)}
*/
public static String getLanguageFromLocaleString(String locale) {
final int idx = locale.indexOf('_');
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
index 577dd64..1557918 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
@@ -33,6 +33,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
public class InputMethodTest extends InstrumentationTestCase {
private static final boolean IS_AUX = true;
@@ -46,84 +47,104 @@ public class InputMethodTest extends InstrumentationTestCase {
private static final Locale LOCALE_EN_IN = new Locale("en", "IN");
private static final Locale LOCALE_HI = new Locale("hi");
private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
+ private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
+ private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
@SmallTest
public void testVoiceImes() throws Exception {
// locale: en_US
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY,
- "DummyDefaultAutoVoiceIme", "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
// locale: en_GB
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
// locale: ja_JP
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
}
@SmallTest
public void testKeyboardImes() throws Exception {
// locale: en_US
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
// locale: en_GB
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
// locale: en_IN
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.hindi");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
+ "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
// locale: hi
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.hindi");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
+ "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
// locale: ja_JP
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.japanese");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.japanese",
+ "com.android.apps.inputmethod.voice");
+
+ // locale: zh_CN
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.pinyin",
+ "com.android.apps.inputmethod.voice");
+
+ // locale: zh_TW
+ // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a
+ // fallback IME regardless of the "default" attribute.
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
}
@SmallTest
public void testParcelable() throws Exception {
- final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes();
+ final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS");
final List<InputMethodInfo> clonedList = cloneViaParcel(originalList);
assertNotNull(clonedList);
final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList);
@@ -139,11 +160,14 @@ public class InputMethodTest extends InstrumentationTestCase {
}
private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
- final Locale systemLocale, final boolean isSystemReady, String... imeNames) {
+ final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
final Context context = getInstrumentation().getTargetContext();
- assertEquals(new HashSet<String>(Arrays.asList(imeNames)),
- getPackageNames(callGetDefaultEnabledImesUnderWithLocale(context,
- isSystemReady, preinstalledImes, systemLocale)));
+ final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesUnderWithLocale(
+ context, isSystemReady, preinstalledImes, systemLocale));
+ assertEquals(expectedImeNames.length, actualImeNames.length);
+ for (int i = 0; i < expectedImeNames.length; ++i) {
+ assertEquals(expectedImeNames[i], actualImeNames[i]);
+ }
}
private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) {
@@ -172,11 +196,10 @@ public class InputMethodTest extends InstrumentationTestCase {
}
}
- private HashSet<String> getPackageNames(final ArrayList<InputMethodInfo> imis) {
- final HashSet<String> packageNames = new HashSet<>();
- for (final InputMethodInfo imi : imis) {
- final String actualPackageName = imi.getPackageName();
- packageNames.add(actualPackageName);
+ private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
+ final String[] packageNames = new String[imis.size()];
+ for (int i = 0; i < imis.size(); ++i) {
+ packageNames[i] = imis.get(i).getPackageName();
}
return packageNames;
}
@@ -278,21 +301,34 @@ public class InputMethodTest extends InstrumentationTestCase {
return preinstalledImes;
}
- private static ArrayList<InputMethodInfo> getSamplePreinstalledImes() {
+ private static boolean contains(final String[] textList, final String textToBeChecked) {
+ if (textList == null) {
+ return false;
+ }
+ for (final String text : textList) {
+ if (Objects.equals(textToBeChecked, text)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) {
ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
// a dummy Voice IME
{
+ final boolean isDefaultIme = false;
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX,
IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice",
- "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, IS_DEFAULT,
+ "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme,
subtypes));
}
-
// a dummy Hindi IME
{
+ final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
// TODO: This subtype should be marked as IS_ASCII_CAPABLE
subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
@@ -300,32 +336,36 @@ public class InputMethodTest extends InstrumentationTestCase {
subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi",
- "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, IS_DEFAULT,
+ "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Pinyin IME
{
+ final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
- preinstalledImes.add(createDummyInputMethodInfo("ccom.android.apps.inputmethod.pinyin",
- "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, IS_DEFAULT,
+ preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin",
+ "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme,
subtypes));
}
- // a dummy Korian IME
+ // a dummy Korean IME
{
+ final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean",
- "com.android.apps.inputmethod.korean", "DummyKorianIme", !IS_AUX, IS_DEFAULT,
+ "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Latin IME
{
+ final boolean isDefaultIme = contains(
+ new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, IS_ASCII_CAPABLE));
@@ -336,12 +376,13 @@ public class InputMethodTest extends InstrumentationTestCase {
subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Japanese IME
{
+ final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
@@ -349,7 +390,7 @@ public class InputMethodTest extends InstrumentationTestCase {
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese",
"com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX,
- IS_DEFAULT, subtypes));
+ isDefaultIme, subtypes));
}
return preinstalledImes;