Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84f20ec4 authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Android (Google) Code Review
Browse files

Merge "Minimize the number of default enabled IMEs part 3" into lmp-dev

parents 85be2722 dc489241
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -416,6 +416,9 @@ public final class InputMethodInfo implements Parcelable {
            return true;
        }
        try {
            if (getIsDefaultResourceId() == 0) {
                return false;
            }
            final Resources res = context.createPackageContext(getPackageName(), 0).getResources();
            return res.getBoolean(getIsDefaultResourceId());
        } catch (NameNotFoundException e) {
+134 −11
Original line number Diff line number Diff line
@@ -53,6 +53,17 @@ public class InputMethodUtils {
    private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
            "EnabledWhenDefaultIsNotAsciiCapable";
    private static final String TAG_ASCII_CAPABLE = "AsciiCapable";
    /**
     * Used in {@link #getFallbackLocaleForDefaultIme(ArrayList, Context)} to find the fallback IMEs
     * that are mainly used until the system becomes ready. Note that {@link Locale} in this array
     * is checked with {@link Locale#equals(Object)}, which means that {@code Locale.ENGLISH}
     * doesn't automatically match {@code Locale("en", "IN")}.
     */
    private static final Locale[] SEARCH_ORDER_OF_FALLBACK_LOCALES = {
        Locale.ENGLISH, // "en"
        Locale.US, // "en_US"
        Locale.UK, // "en_GB"
    };

    private InputMethodUtils() {
        // This utility class is not publicly instantiable.
@@ -102,6 +113,11 @@ public class InputMethodUtils {
                & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

    /**
     * @deprecated Use {@link Locale} returned from
     * {@link #getFallbackLocaleForDefaultIme(ArrayList)} instead.
     */
    @Deprecated
    public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) {
        if (!isSystemIme(imi)) {
            return false;
@@ -109,6 +125,21 @@ public class InputMethodUtils {
        return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD);
    }

    public static Locale getFallbackLocaleForDefaultIme(final ArrayList<InputMethodInfo> imis,
            final Context context) {
        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)) {
                    return fallbackLocale;
                }
            }
        }
        return null;
    }

    private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(InputMethodInfo imi) {
        if (!isSystemIme(imi)) {
            return false;
@@ -126,37 +157,85 @@ public class InputMethodUtils {
        return false;
    }

    public static Locale getSystemLocaleFromContext(final Context context) {
        try {
            return context.getResources().getConfiguration().locale;
        } catch (Resources.NotFoundException ex) {
            return null;
        }
    }

    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);

        if (!isSystemReady) {
            final ArrayList<InputMethodInfo> retval = new ArrayList<>();
            for (int i = 0; i < imis.size(); ++i) {
                final InputMethodInfo imi = imis.get(i);
                if (isSystemImeThatHasEnglishKeyboardSubtype(imi)) {
                // 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);
                }
            }
            return retval;
        }

        // 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 auxilialyImeAdded = false;
        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);
            // TODO: We should check isAsciiCapable instead of relying on
            // isSystemImeThatHasEnglishKeyboardSubtype().
            if (isValidSystemDefaultIme(isSystemReady, imi, context)
                    || isSystemImeThatHasEnglishKeyboardSubtype(imi)) {
            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);
                if (imi.isAuxiliaryIme()) {
                    auxilialyImeAdded = true;
            }
            systemLocaleKeyboardImeFound |= isSystemLocaleKeyboardIme;
        }

        // System locale country doesn't match any IMEs, try to find IMEs in a country-agnostic
        // way.
        if (!systemLocaleKeyboardImeFound) {
            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 (isImeThatHasSubtypeOf(imi, systemLocale, true /* ignoreCountry */,
                        SUBTYPE_MODE_ANY)) {
                    retval.add(imi);
                }
            }
        }

        // If one or more auxiliary input methods are available, OK to stop populating the list.
        if (auxilialyImeAdded) {
        for (int i = 0; i < retval.size(); ++i) {
            if (retval.get(i).isAuxiliaryIme()) {
                return retval;
            }
        }
        for (int i = 0; i < imis.size(); ++i) {
            final InputMethodInfo imi = imis.get(i);
            if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi)) {
@@ -166,7 +245,20 @@ public class InputMethodUtils {
        return retval;
    }

    // TODO: Rename isSystemDefaultImeThatHasCurrentLanguageSubtype
    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);
    }

    /**
     * @deprecated Use {@link #isSystemIme(InputMethodInfo)} and
     * {@link InputMethodInfo#isDefault(Context)} and
     * {@link #isImeThatHasSubtypeOf(InputMethodInfo, Locale, boolean, String))} instead.
     */
    @Deprecated
    public static boolean isValidSystemDefaultIme(
            boolean isSystemReady, InputMethodInfo imi, Context context) {
        if (!isSystemReady) {
@@ -191,6 +283,36 @@ public class InputMethodUtils {
        return false;
    }

    public static boolean containsSubtypeOf(final InputMethodInfo imi,
            final Locale locale, final boolean ignoreCountry, final String mode) {
        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 {
                // TODO: Use {@link Locale#toLanguageTag()} and
                // {@link Locale#forLanguageTag(languageTag)} instead.
                if (!TextUtils.equals(subtype.getLocale(), locale.toString())) {
                    continue;
                }
            }
            if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode) ||
                    mode.equalsIgnoreCase(subtype.getMode())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @deprecated Use {@link #containsSubtypeOf(InputMethodInfo, Locale, boolean, String)} instead.
     */
    @Deprecated
    public static boolean containsSubtypeOf(InputMethodInfo imi, String language, String mode) {
        final int N = imi.getSubtypeCount();
        for (int i = 0; i < N; ++i) {
@@ -354,6 +476,7 @@ public class InputMethodUtils {

    /**
     * Returns the language component of a given locale string.
     * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}
     */
    public static String getLanguageFromLocaleString(String locale) {
        final int idx = locale.indexOf('_');
+8 −11
Original line number Diff line number Diff line
@@ -79,7 +79,6 @@ public class InputMethodTest extends InstrumentationTestCase {
        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY,
                "DummyDefaultEnKeyboardIme");
        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
                "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
                "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
                "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
@@ -90,38 +89,36 @@ public class InputMethodTest extends InstrumentationTestCase {
    public void testKeyboardImes() throws Exception {
        // locale: en_US
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, !IS_SYSTEM_READY,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.hindi");
                "com.android.apps.inputmethod.latin");
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, IS_SYSTEM_READY,
                "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
                "com.android.apps.inputmethod.hindi");
                "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");

        // locale: en_GB
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, !IS_SYSTEM_READY,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.hindi");
                "com.android.apps.inputmethod.latin");
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, IS_SYSTEM_READY,
                "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
                "com.android.apps.inputmethod.hindi");
                "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");

        // locale: en_IN
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, !IS_SYSTEM_READY,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.hindi");
                "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");

        // locale: hi
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, !IS_SYSTEM_READY,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.hindi");
                "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");

        // locale: ja_JP
        assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, !IS_SYSTEM_READY,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.hindi");
                "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.hindi", "com.android.apps.inputmethod.japanese");
                "com.android.apps.inputmethod.japanese");
    }

    @SmallTest