From f72e8f41f417f995c38d9f19c83b835d176516fa Mon Sep 17 00:00:00 2001 From: Helium314 Date: Sat, 24 May 2025 12:45:36 +0200 Subject: [PATCH] make SubtypeLocaleUtils less convoluted still harder to understand than necessary... --- .../MainKeyboardAccessibilityDelegate.kt | 6 +- .../keyboard/keyboard/KeyboardLayoutSet.java | 2 +- .../keyboard/latin/RichInputMethodSubtype.kt | 17 +-- .../latin/utils/SubtypeLocaleUtils.kt | 114 +++++++----------- .../settings/screens/SubtypeScreen.kt | 2 +- 5 files changed, 52 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt b/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt index 0be99c7c0..6a4283494 100644 --- a/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt +++ b/app/src/main/java/helium314/keyboard/accessibility/MainKeyboardAccessibilityDelegate.kt @@ -14,7 +14,7 @@ import android.view.MotionEvent import helium314.keyboard.accessibility.AccessibilityLongPressTimer.LongPressTimerCallback import helium314.keyboard.keyboard.* import helium314.keyboard.latin.R -import helium314.keyboard.latin.utils.SubtypeLocaleUtils +import helium314.keyboard.latin.utils.SubtypeLocaleUtils.displayName /** * This class represents a delegate that can be registered in [MainKeyboardView] to enhance @@ -86,9 +86,7 @@ class MainKeyboardAccessibilityDelegate( * @param keyboard The new keyboard. */ private fun announceKeyboardLanguage(keyboard: Keyboard) { - val languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( - keyboard.mId.mSubtype.rawSubtype) - sendWindowStateChanged(languageText) + sendWindowStateChanged(keyboard.mId.mSubtype.rawSubtype.displayName()) } /** diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java index 3344aa6eb..2bdbbdc97 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java @@ -96,7 +96,7 @@ public final class KeyboardLayoutSet { public static void onSystemLocaleChanged() { clearKeyboardCache(); LocaleKeyboardInfosKt.clearCache(); - SubtypeLocaleUtils.clearDisplayNameCache(); + SubtypeLocaleUtils.clearSubtypeDisplayNameCache(); } public static void onKeyboardThemeChanged() { diff --git a/app/src/main/java/helium314/keyboard/latin/RichInputMethodSubtype.kt b/app/src/main/java/helium314/keyboard/latin/RichInputMethodSubtype.kt index c26f79df8..01daa8190 100644 --- a/app/src/main/java/helium314/keyboard/latin/RichInputMethodSubtype.kt +++ b/app/src/main/java/helium314/keyboard/latin/RichInputMethodSubtype.kt @@ -17,7 +17,6 @@ import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.SubtypeLocaleUtils import helium314.keyboard.latin.utils.locale -import helium314.keyboard.latin.utils.mainLayoutNameOrQwerty import java.util.Locale /** @@ -42,21 +41,9 @@ class RichInputMethodSubtype private constructor(val rawSubtype: InputMethodSubt val isCustom: Boolean get() = LayoutUtilsCustom.isCustomLayout(mainLayoutName) - val fullDisplayName: String get() { - if (isNoLanguage) { - return SubtypeLocaleUtils.getMainLayoutDisplayName(rawSubtype.mainLayoutNameOrQwerty())!! - } - return SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale) - } + val fullDisplayName: String get() = SubtypeLocaleUtils.getSubtypeLocaleDisplayName(locale) - val middleDisplayName: String - // Get the RichInputMethodSubtype's middle display name in its locale. - get() { - if (isNoLanguage) { - return SubtypeLocaleUtils.getMainLayoutDisplayName(rawSubtype.mainLayoutNameOrQwerty())!! - } - return SubtypeLocaleUtils.getSubtypeLanguageDisplayName(locale) - } + val middleDisplayName: String get() = SubtypeLocaleUtils.getSubtypeLanguageDisplayName(locale) override fun equals(other: Any?): Boolean { if (other !is RichInputMethodSubtype) return false diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.kt index 731da10a7..2c2b88e02 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeLocaleUtils.kt @@ -40,7 +40,7 @@ object SubtypeLocaleUtils { // Exceptional locale to subtype name with layout resource id map. private val exceptionalLocaleToWithLayoutNameIds = HashMap() - private val resourceSubtypeDisplayNames = HashMap() + private val resourceSubtypeDisplayNameCache = HashMap() // Note that this initialization method can be called multiple times. @JvmStatic @@ -114,6 +114,7 @@ object SubtypeLocaleUtils { return keyboardLayoutToNameIds[key] ?: UNKNOWN_KEYBOARD_LAYOUT } + /** Usually the [locale], but Locale.ROOT for exceptionalLocaleDisplayedInRootLocale, and system locale for NO_LANGUAGE */ private fun getDisplayLocaleOfSubtypeLocale(locale: Locale): Locale { val languageTag = locale.toLanguageTag() if (languageTag == NO_LANGUAGE) @@ -123,16 +124,13 @@ object SubtypeLocaleUtils { return locale } - fun getSubtypeLocaleDisplayNameInSystemLocale(locale: Locale): String { - val displayLocale = resources.configuration.locale() - return getSubtypeLocaleDisplayNameInternal(locale, displayLocale) - } - + /** Returns the full locale display name for use on space bar (considers exceptionalLocaleDisplayedInRootLocale) */ fun getSubtypeLocaleDisplayName(locale: Locale): String { val displayLocale = getDisplayLocaleOfSubtypeLocale(locale) return getSubtypeLocaleDisplayNameInternal(locale, displayLocale) } + /** Returns the language display name for use on space bar (considers exceptionalLocaleDisplayedInRootLocale) */ fun getSubtypeLanguageDisplayName(locale: Locale): String { val languageLocale = if (exceptionalLocaleDisplayedInRootLocale.containsKey(locale.toLanguageTag())) locale @@ -141,15 +139,17 @@ object SubtypeLocaleUtils { return getSubtypeLocaleDisplayNameInternal(languageLocale, getDisplayLocaleOfSubtypeLocale(locale)) } + /** + * Display name of subtype [locale] in [displayLocale]. + * Considers exceptionalLocaleDisplayedInRootLocale and exceptionalLocaleToNameIds, defaults to Locale.localizedDisplayName. + */ private fun getSubtypeLocaleDisplayNameInternal(locale: Locale, displayLocale: Locale): String { val languageTag = locale.toLanguageTag() if (languageTag == NO_LANGUAGE) { // "No language" subtype should be displayed in system locale. return resources.getString(R.string.subtype_no_language) } - val exceptionalNameResId = if (displayLocale == Locale.ROOT - && exceptionalLocaleDisplayedInRootLocale.containsKey(languageTag) - ) + val exceptionalNameResId = if (displayLocale == Locale.ROOT && exceptionalLocaleDisplayedInRootLocale.containsKey(languageTag)) exceptionalLocaleDisplayedInRootLocale[languageTag] else exceptionalLocaleToNameIds[languageTag] @@ -161,51 +161,9 @@ object SubtypeLocaleUtils { return StringUtils.capitalizeFirstCodePoint(displayName, displayLocale) } - // InputMethodSubtype's display name in its locale. - // isAdditionalSubtype (T=true, F=false) - // locale layout | display name - // ------ ------- - ---------------------- - // en_US qwerty F English (US) exception - // en_GB qwerty F English (UK) exception - // es_US spanish F Español (EE.UU.) exception - // fr azerty F Français - // fr_CA qwerty F Français (Canada) - // fr_CH swiss F Français (Suisse) - // de qwertz F Deutsch - // de_CH swiss T Deutsch (Schweiz) - // zz qwerty F Alphabet (QWERTY) in system locale - // fr qwertz T Français (QWERTZ) - // de qwerty T Deutsch (QWERTY) - // en_US azerty T English (US) (AZERTY) exception - // zz azerty T Alphabet (AZERTY) in system locale - private fun getReplacementString(subtype: InputMethodSubtype, displayLocale: Locale): String = - subtype.getExtraValueOf(ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME) - ?: getSubtypeLocaleDisplayNameInternal(subtype.locale(), displayLocale) - - fun getDisplayNameInSystemLocale(mainLayoutName: String, locale: Locale): String { - getMainLayoutDisplayName(mainLayoutName)?.let { return it } // works for custom and latin layouts - - // we have some locale-specific layout - for (subtype in SubtypeSettings.getResourceSubtypesForLocale(locale)) { - if (mainLayoutName == getMainLayoutFromExtraValue(subtype.extraValue)) - return getSubtypeDisplayNameInSystemLocale(subtype) - } - return mainLayoutName // should never happen... - } - - fun getSubtypeDisplayNameInSystemLocale(subtype: InputMethodSubtype): String { - resourceSubtypeDisplayNames[subtype.hashCode()]?.let { return it } - - val displayName = getSubtypeDisplayNameInternal(subtype, resources.configuration.locale()) - if (!subtype.containsExtraValueKey(ExtraValue.IS_ADDITIONAL_SUBTYPE)) { - resourceSubtypeDisplayNames[subtype.hashCode()] = displayName - } - return displayName - } - @JvmStatic - fun clearDisplayNameCache() { - resourceSubtypeDisplayNames.clear() + fun clearSubtypeDisplayNameCache() { + resourceSubtypeDisplayNameCache.clear() } @JvmStatic @@ -216,27 +174,48 @@ object SubtypeLocaleUtils { return subtype.locale().toString() + "/" + subtype.mainLayoutNameOrQwerty() } - private fun getSubtypeDisplayNameInternal(subtype: InputMethodSubtype, displayLocale: Locale): String { - val replacementString = getReplacementString(subtype, displayLocale) - return runInLocale(resources, displayLocale) { res: Resources -> - try { - StringUtils.capitalizeFirstCodePoint(res.getString(subtype.nameResId, replacementString), displayLocale) - } catch (e: Resources.NotFoundException) { - Log.w(TAG, ("Unknown subtype: mode=${subtype.mode} nameResId=${subtype.nameResId} locale=${subtype.locale()} extra=${subtype.extraValue}\n${DebugLogUtils.getStackTrace()}")) - "" - } + /** Subtype display name is (), defaults to system locale */ + fun InputMethodSubtype.displayName(displayLocale: Locale? = null): String { + if (displayLocale == null) resourceSubtypeDisplayNameCache[hashCode()]?.let { return it } + + val layoutName = mainLayoutName() + if (layoutName != null && LayoutUtilsCustom.isCustomLayout(layoutName)) { + return resources.getString( + R.string.subtype_with_layout_generic, + locale().localizedDisplayName(resources, displayLocale), + LayoutUtilsCustom.getDisplayName(layoutName) + ) } + + val actualDisplayLocale = displayLocale ?: resources.configuration.locale() + // replacement for %s in nameResId + // this is usually the locale, but can also include a subtype name when subtype_generic is used + val replacementString = getExtraValueOf(ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME) + ?: getSubtypeLocaleDisplayNameInternal(locale(), actualDisplayLocale) + + val name = runCatching { + if (displayLocale == null) resources.getString(nameResId, replacementString) + else runInLocale(resources, displayLocale) { resources.getString(nameResId, replacementString) } + }.getOrNull() ?: locale().localizedDisplayName(resources, displayLocale) + val displayName = StringUtils.capitalizeFirstCodePoint(name, actualDisplayLocale) + if (displayLocale == null && !containsExtraValueKey(ExtraValue.IS_ADDITIONAL_SUBTYPE)) + resourceSubtypeDisplayNameCache[hashCode()] = displayName + return displayName } fun getMainLayoutDisplayName(layoutName: String): String? = if (LayoutUtilsCustom.isCustomLayout(layoutName)) LayoutUtilsCustom.getDisplayName(layoutName) else keyboardLayoutToDisplayName[layoutName] - fun InputMethodSubtype.displayName(): String { - val layoutName = mainLayoutNameOrQwerty() - if (LayoutUtilsCustom.isCustomLayout(layoutName)) - return "${locale().localizedDisplayName(resources)} (${LayoutUtilsCustom.getDisplayName(layoutName)})" - return getSubtypeDisplayNameInSystemLocale(this) + fun getLayoutDisplayNameInSystemLocale(mainLayoutName: String, locale: Locale): String { + getMainLayoutDisplayName(mainLayoutName)?.let { return it } // works for custom and latin layouts + + // we have some locale-specific layout, use the subtype name + for (subtype in SubtypeSettings.getResourceSubtypesForLocale(locale)) { + if (mainLayoutName == getMainLayoutFromExtraValue(subtype.extraValue)) + return subtype.displayName() + } + return mainLayoutName // should never happen... } @JvmStatic @@ -248,7 +227,6 @@ object SubtypeLocaleUtils { const val EMOJI = "emoji" val UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic - private val TAG = SubtypeLocaleUtils::class.java.simpleName private const val SUBTYPE_NAME_RESOURCE_PREFIX = "string/subtype_" private const val SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = "string/subtype_generic_" private const val SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX = "string/subtype_with_layout_" diff --git a/app/src/main/java/helium314/keyboard/settings/screens/SubtypeScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/SubtypeScreen.kt index 903e11aab..336a192eb 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/SubtypeScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/SubtypeScreen.kt @@ -407,7 +407,7 @@ private fun MainLayoutRow( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.widthIn(min = 200.dp).fillMaxWidth() ) { - Text(SubtypeLocaleUtils.getDisplayNameInSystemLocale(it, currentSubtype.locale)) + Text(SubtypeLocaleUtils.getLayoutDisplayNameInSystemLocale(it, currentSubtype.locale)) Row (verticalAlignment = Alignment.CenterVertically) { IconButton({ showLayoutEditDialog = it to null }) { Icon(painterResource(R.drawable.ic_edit), stringResource(R.string.edit_layout)) } if (it in customLayouts)