From 1dc53663f012a7a2b5fb77f480ec0cc4d7d04f6a Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 17 Oct 2023 15:01:02 +0200 Subject: [PATCH] more exhaustive search for hinLocales, if present --- .../compat/EditorInfoCompatUtils.kt | 44 +++++-------------- .../compat/LocaleListCompatUtils.kt | 22 ---------- .../keyboard/KeyboardLayoutSet.java | 3 +- .../openboard/inputmethod/latin/LatinIME.java | 18 ++++---- .../latin/RichInputMethodManager.java | 43 +++++++++++++++++- 5 files changed, 62 insertions(+), 68 deletions(-) delete mode 100644 app/src/main/java/org/dslul/openboard/inputmethod/compat/LocaleListCompatUtils.kt diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/compat/EditorInfoCompatUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/compat/EditorInfoCompatUtils.kt index af5761bd1..c9ef5bd9f 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/compat/EditorInfoCompatUtils.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/compat/EditorInfoCompatUtils.kt @@ -6,20 +6,12 @@ package org.dslul.openboard.inputmethod.compat +import android.os.Build import android.view.inputmethod.EditorInfo import java.util.* +import kotlin.collections.ArrayList object EditorInfoCompatUtils { - // Note that EditorInfo.IME_FLAG_FORCE_ASCII has been introduced - // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). - private val FIELD_IME_FLAG_FORCE_ASCII = CompatUtils.getField(EditorInfo::class.java, "IME_FLAG_FORCE_ASCII") - private val OBJ_IME_FLAG_FORCE_ASCII: Int? = CompatUtils.getFieldValue(null, null, FIELD_IME_FLAG_FORCE_ASCII) as? Int - private val FIELD_HINT_LOCALES = CompatUtils.getField(EditorInfo::class.java, "hintLocales") - - @JvmStatic - fun hasFlagForceAscii(imeOptions: Int): Boolean { - return if (OBJ_IME_FLAG_FORCE_ASCII == null) false else imeOptions and OBJ_IME_FLAG_FORCE_ASCII != 0 - } @JvmStatic fun imeActionName(imeOptions: Int): String { @@ -36,32 +28,16 @@ object EditorInfoCompatUtils { } } - fun imeOptionsName(imeOptions: Int): String { - val action = imeActionName(imeOptions) - val flags = StringBuilder() - if (imeOptions and EditorInfo.IME_FLAG_NO_ENTER_ACTION != 0) { - flags.append("flagNoEnterAction|") - } - if (imeOptions and EditorInfo.IME_FLAG_NAVIGATE_NEXT != 0) { - flags.append("flagNavigateNext|") - } - if (imeOptions and EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS != 0) { - flags.append("flagNavigatePrevious|") - } - if (hasFlagForceAscii(imeOptions)) { - flags.append("flagForceAscii|") - } - return flags.toString() + action - } - @JvmStatic - fun getPrimaryHintLocale(editorInfo: EditorInfo?): Locale? { - if (editorInfo == null) { + fun getHintLocales(editorInfo: EditorInfo?): List? { + if (editorInfo == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return null } - val localeList = CompatUtils.getFieldValue(editorInfo, null, FIELD_HINT_LOCALES) ?: return null - return if (LocaleListCompatUtils.isEmpty(localeList)) { - null - } else LocaleListCompatUtils[localeList, 0] + val localeList = editorInfo.hintLocales ?: return null + val locales = ArrayList(localeList.size()) + for (i in 0 until localeList.size()) { + locales[i] = localeList.get(i) + } + return locales } } \ No newline at end of file diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/compat/LocaleListCompatUtils.kt b/app/src/main/java/org/dslul/openboard/inputmethod/compat/LocaleListCompatUtils.kt deleted file mode 100644 index 37ec6db2a..000000000 --- a/app/src/main/java/org/dslul/openboard/inputmethod/compat/LocaleListCompatUtils.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * modified - * SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only - */ - -package org.dslul.openboard.inputmethod.compat - -import java.util.* - -object LocaleListCompatUtils { - private val CLASS_LocaleList = CompatUtils.getClass("android.os.LocaleList") - private val METHOD_get = CompatUtils.getMethod(CLASS_LocaleList, "get", Int::class.javaPrimitiveType) - private val METHOD_isEmpty = CompatUtils.getMethod(CLASS_LocaleList, "isEmpty") - fun isEmpty(localeList: Any?): Boolean { - return CompatUtils.invoke(localeList, java.lang.Boolean.FALSE, METHOD_isEmpty) as Boolean - } - - operator fun get(localeList: Any?, index: Int): Locale? { - return CompatUtils.invoke(localeList, null, METHOD_get, index) as Locale - } -} \ No newline at end of file diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java index 445e8bf95..618ac2001 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardLayoutSet.java @@ -290,8 +290,7 @@ public final class KeyboardLayoutSet { // TODO: Consolidate with {@link InputAttributes}. @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( mPackageName, FORCE_ASCII, mParams.mEditorInfo); - final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( - mParams.mEditorInfo.imeOptions) + final boolean forceAscii = (mParams.mEditorInfo.imeOptions & EditorInfo.IME_FLAG_FORCE_ASCII) != 0 || deprecatedForceAscii; final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable) ? RichInputMethodSubtype.getNoLanguageSubtype() diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java index 4fd12c3c2..5811441bd 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java @@ -897,18 +897,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInput(editorInfo, restarting); - // If the primary hint language does not match the current subtype language, then try - // to switch to the primary hint language. - // TODO: Support all the locales in EditorInfo#hintLocales. - final Locale primaryHintLocale = EditorInfoCompatUtils.getPrimaryHintLocale(editorInfo); - if (primaryHintLocale == null) { + final List hintLocales = EditorInfoCompatUtils.getHintLocales(editorInfo); + if (hintLocales == null) { return; } - final InputMethodSubtype newSubtype = mRichImm.findSubtypeByLocale(primaryHintLocale); - if (newSubtype == null || newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype())) { - return; + // Try switching to a subtype matching the hint language. + for (final Locale hintLocale : hintLocales) { + final InputMethodSubtype newSubtype = mRichImm.findSubtypeByLocale(hintLocale); + if (newSubtype == null) continue; + if (newSubtype.equals(mRichImm.getCurrentSubtype().getRawSubtype())) + return; // no need to switch, we already use the correct locale + mHandler.postSwitchLanguage(newSubtype); } - mHandler.postSwitchLanguage(newSubtype); } @SuppressWarnings("deprecation") diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/RichInputMethodManager.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/RichInputMethodManager.java index 18e4fe0d3..6a1fec885 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/RichInputMethodManager.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/RichInputMethodManager.java @@ -18,9 +18,11 @@ import android.view.inputmethod.InputMethodSubtype; import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.compat.InputMethodSubtypeCompatUtils; +import org.dslul.openboard.inputmethod.latin.settings.Settings; import org.dslul.openboard.inputmethod.latin.settings.SubtypeSettingsKt; import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils; import org.dslul.openboard.inputmethod.latin.utils.LanguageOnSpacebarUtils; +import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; import org.dslul.openboard.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Collections; @@ -308,10 +310,10 @@ public class RichInputMethodManager { public InputMethodSubtype findSubtypeByLocale(final Locale locale) { // Find the best subtype based on a straightforward matching algorithm. - // TODO: Use LocaleList#getFirstMatch() instead. final List subtypes = getMyEnabledInputMethodSubtypeList(true /* allowsImplicitlySelectedSubtypes */); final int count = subtypes.size(); + // search for exact match for (int i = 0; i < count; ++i) { final InputMethodSubtype subtype = subtypes.get(i); final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype); @@ -319,6 +321,7 @@ public class RichInputMethodManager { return subtype; } } + // search for language + country + variant match for (int i = 0; i < count; ++i) { final InputMethodSubtype subtype = subtypes.get(i); final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype); @@ -328,6 +331,7 @@ public class RichInputMethodManager { return subtype; } } + // search for language + country match for (int i = 0; i < count; ++i) { final InputMethodSubtype subtype = subtypes.get(i); final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype); @@ -336,6 +340,19 @@ public class RichInputMethodManager { return subtype; } } + // search for secondary locale match + final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(mContext); + for (int i = 0; i < count; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final String subtypeLocale = subtype.getLocale(); + final List secondaryLocales = Settings.getSecondaryLocales(prefs, subtypeLocale); + for (final Locale secondaryLocale : secondaryLocales) { + if (secondaryLocale.equals(locale)) { + return subtype; + } + } + } + // search for language match for (int i = 0; i < count; ++i) { final InputMethodSubtype subtype = subtypes.get(i); final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype); @@ -343,6 +360,30 @@ public class RichInputMethodManager { return subtype; } } + // search for secondary language match + for (int i = 0; i < count; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final String subtypeLocale = subtype.getLocale(); + final List secondaryLocales = Settings.getSecondaryLocales(prefs, subtypeLocale); + for (final Locale secondaryLocale : secondaryLocales) { + if (secondaryLocale.getLanguage().equals(locale.getLanguage())) { + return subtype; + } + } + } + + // extra: if current script is not compatible to current subtype, search for compatible script + // this is acceptable only because this function is only used for switching to a certain locale using EditorInfo.hintLocales + final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale); + if (script != ScriptUtils.getScriptFromSpellCheckerLocale(getCurrentSubtypeLocale())) { + for (int i = 0; i < count; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final Locale subtypeLocale = InputMethodSubtypeCompatUtils.getLocaleObject(subtype); + if (ScriptUtils.getScriptFromSpellCheckerLocale(subtypeLocale) == script) { + return subtype; + } + } + } return null; }