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

Commit 9c372194 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Make sure at least one non-aux IME is enabled

Imagine the scenario where three IMEs are installed, and only the last
two are enabled:

 IME A:
  * a pre-installed IME
  * has a subtype [mode="keyboard" && isAuxiliary=false]
  * disabled by user (hence not included in ENABLED_INPUT_METHODS)
 IME B:
  * a pre-installed IME
  * has a subtype [isAuxiliary=true]
  * currently enabled (included in ENABLED_INPUT_METHODS)
 IME X:
  * not a pre-installed IME
  * has a subtype [mode="keyboard" && isAuxiliary=false]
  * currently enabled (included in ENABLED_INPUT_METHODS)

In this scenario, when the IME X is uninstalled, the current
implementation of InputMethodManagerService (IMMS) does not try to
hard reset enabled IMEs because there is still one enabled IME, even
though it is an auxiliary IME.

This can, however, be problematic because an auxiliary IME is just a
supplemental IME and user may not be able to easily access the UI to
re-enable non-auxiliary IME such as IME A.  For instance, on the lock
screen there is no way to manually re-enable the IME A.

With this CL, every time the available IME list is updated, IMMS
ensures that at least one non-auxiliary IME is enabled.  If no
non-auxiliary IME is enabled, then IMMS tries to pick up one from the
pre-installed IME by using the same logic when choosing the default
enabled IMEs for the hard-reset scenario.

Bug: 71509065
Fix: 71509065
Test: atest FrameworksCoreTests:com.android.internal.inputmethod.InputMethodUtilsTest
Test: Manually verified in the above scenario
Change-Id: I88c69f548526b35f0e4ba37489365b2433373b04
parent 37854178
Loading
Loading
Loading
Loading
+15 −7
Original line number Diff line number Diff line
@@ -320,8 +320,8 @@ public class InputMethodUtils {
        return builder;
    }

    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context,
            final ArrayList<InputMethodInfo> imis) {
    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
            Context context, ArrayList<InputMethodInfo> imis, boolean onlyMinimum) {
        final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
        // We will primarily rely on the system locale, but also keep relying on the fallback locale
        // as a last resort.
@@ -329,11 +329,19 @@ public class InputMethodUtils {
        // 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,
        final InputMethodListBuilder builder =
                getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale);
        if (!onlyMinimum) {
            builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
                    true /* checkCountry */, SUBTYPE_MODE_ANY)
                .fillAuxiliaryImes(imis, context)
                .build();
                    .fillAuxiliaryImes(imis, context);
        }
        return builder.build();
    }

    public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
            Context context, ArrayList<InputMethodInfo> imis) {
        return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
    }

    public static Locale constructLocaleFromString(String localeStr) {
+39 −0
Original line number Diff line number Diff line
@@ -99,6 +99,10 @@ public class InputMethodUtilsTest {
        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
                "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                "DummyNonDefaultAutoVoiceIme1");
        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
                "DummyDefaultEnKeyboardIme");
        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
                "DummyDefaultEnKeyboardIme");

        // locale: en_GB
        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
@@ -106,6 +110,10 @@ public class InputMethodUtilsTest {
        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
                "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                "DummyNonDefaultAutoVoiceIme1");
        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
                "DummyDefaultEnKeyboardIme");
        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
                "DummyDefaultEnKeyboardIme");

        // locale: ja_JP
        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
@@ -113,6 +121,10 @@ public class InputMethodUtilsTest {
        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
                "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
                "DummyNonDefaultAutoVoiceIme1");
        assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
                "DummyDefaultEnKeyboardIme");
        assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
                "DummyDefaultEnKeyboardIme");
    }

    @Test
@@ -120,34 +132,49 @@ public class InputMethodUtilsTest {
        // locale: en_US
        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
                "com.android.apps.inputmethod.latin");

        // locale: en_GB
        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
                "com.android.apps.inputmethod.latin");

        // locale: en_IN
        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
                "com.android.apps.inputmethod.hindi",
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
                "com.android.apps.inputmethod.hindi",
                "com.android.apps.inputmethod.latin");

        // locale: hi
        assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
                "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin",
                "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
                "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin");

        // locale: ja_JP
        assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
                "com.android.apps.inputmethod.japanese", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
                "com.android.apps.inputmethod.japanese");

        // locale: zh_CN
        assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
                "com.android.apps.inputmethod.pinyin", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
                "com.android.apps.inputmethod.pinyin");

        // 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,
                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
        assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
                "com.android.apps.inputmethod.latin");
    }

    @Test
@@ -785,6 +812,18 @@ public class InputMethodUtilsTest {
        }
    }

    private void assertDefaultEnabledMinimumImes(final ArrayList<InputMethodInfo> preinstalledImes,
            final Locale systemLocale, String... expectedImeNames) {
        final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
        final String[] actualImeNames = getPackageNames(
                InputMethodUtils.getDefaultEnabledImes(context, preinstalledImes,
                        true /* onlyMinimum */));
        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) {
        Parcel p = null;
        try {
+14 −3
Original line number Diff line number Diff line
@@ -3663,30 +3663,41 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
            }
        }

        boolean reenableMinimumNonAuxSystemImes = false;
        // TODO: The following code should find better place to live.
        if (!resetDefaultEnabledIme) {
            boolean enabledImeFound = false;
            boolean enabledNonAuxImeFound = false;
            final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodListLocked();
            final int N = enabledImes.size();
            for (int i = 0; i < N; ++i) {
                final InputMethodInfo imi = enabledImes.get(i);
                if (mMethodList.contains(imi)) {
                    enabledImeFound = true;
                    if (!imi.isAuxiliaryIme()) {
                        enabledNonAuxImeFound = true;
                        break;
                    }
                }
            }
            if (!enabledImeFound) {
                if (DEBUG) {
                    Slog.i(TAG, "All the enabled IMEs are gone. Reset default enabled IMEs.");
                }
                resetDefaultEnabledIme = true;
                resetSelectedInputMethodAndSubtypeLocked("");
            } else if (!enabledNonAuxImeFound) {
                if (DEBUG) {
                    Slog.i(TAG, "All the enabled non-Aux IMEs are gone. Do partial reset.");
                }
                reenableMinimumNonAuxSystemImes = true;
            }
        }

        if (resetDefaultEnabledIme) {
        if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
            final ArrayList<InputMethodInfo> defaultEnabledIme =
                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList,
                            reenableMinimumNonAuxSystemImes);
            final int N = defaultEnabledIme.size();
            for (int i = 0; i < N; ++i) {
                final InputMethodInfo imi =  defaultEnabledIme.get(i);