diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ab2de7e01..ac6e9b481 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -15,7 +15,7 @@ tl;dr: **Describe the bug** **To Reproduce** -If possible, provide all the necessary steps to reproduce your problem. +If possible, provide all the necessary steps to reproduce your problem, including the involved apps or settings if relevant. **Expected behavior** If it's not obvious (e.g. not crash), describe how you think the app should behave. diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt index fabae1290..a98a4beb4 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/KeyboardParser.kt @@ -4,13 +4,9 @@ package helium314.keyboard.keyboard.internal.keyboard_parser import android.content.Context import android.content.res.Configuration import helium314.keyboard.latin.utils.Log -import android.view.inputmethod.EditorInfo -import androidx.annotation.StringRes import helium314.keyboard.keyboard.Key import helium314.keyboard.keyboard.Key.KeyParams import helium314.keyboard.keyboard.KeyboardId -import helium314.keyboard.keyboard.KeyboardTheme -import helium314.keyboard.keyboard.internal.KeyboardIconsSet import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyLabel @@ -18,20 +14,15 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyType import helium314.keyboard.keyboard.internal.keyboard_parser.floris.SimplePopups import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData import helium314.keyboard.latin.R -import helium314.keyboard.latin.common.Constants -import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.isEmoji import helium314.keyboard.latin.define.DebugFlags import helium314.keyboard.latin.settings.Settings -import helium314.keyboard.latin.spellcheck.AndroidSpellCheckerService -import helium314.keyboard.latin.utils.InputTypeUtils import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT import helium314.keyboard.latin.utils.POPUP_KEYS_NUMBER import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.removeFirst import helium314.keyboard.latin.utils.replaceFirst -import helium314.keyboard.latin.utils.runInLocale import helium314.keyboard.latin.utils.splitAt import helium314.keyboard.latin.utils.sumOf @@ -277,31 +268,12 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co return functionalKeysLeft to functionalKeysRight } - // this is not nice in here, but otherwise we'd need context, and defaultLabelFlags and infos for toKeyParams - // improve it later, but currently this messy way is still ok + // todo: try moving defaultLabelFlags and layoutInfo into KeyboardParams private fun KeyData.processFunctionalKeys(): KeyData? = when (label) { // todo: why defaultLabelFlags exactly here? is this for armenian or bengali period labels? try removing also check in holo theme KeyLabel.PERIOD -> copy(newLabelFlags = labelFlags or defaultLabelFlags) KeyLabel.SHIFT -> if (infos.hasShiftKey) this else null - KeyLabel.ACTION -> copy( - // todo: evaluating the label should actually only happen in toKeyParams - // this label change already makes it necessary to provide the background in here too, because toKeyParams can't use action as label - newLabel = "${getActionKeyLabel()}|${getActionKeyCode()}", - newPopup = popup.merge(getActionKeyPopupKeys()?.let { SimplePopups(it) }), - // the label change is messing with toKeyParams, so we need to supply the appropriate BG type here - newType = type ?: KeyType.ENTER_EDITING, - newLabelFlags = Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or - Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or - Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) - ) - else -> { - // this is ugly... - if (label.length > 8 && label.startsWith("!string/")) { - val id = context.resources.getIdentifier(label.substringAfter("!string/"), "string", context.packageName) - if (id != 0) copy(newLabel = getInLocale(id)) - else this - } else this - } + else -> this } private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { @@ -340,122 +312,6 @@ class KeyboardParser(private val params: KeyboardParams, private val context: Co } } - private fun getActionKeyLabel(): String { - if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) - return "!icon/enter_key" - val iconName = when (params.mId.imeAction()) { - EditorInfo.IME_ACTION_GO -> KeyboardIconsSet.NAME_GO_KEY - EditorInfo.IME_ACTION_SEARCH -> KeyboardIconsSet.NAME_SEARCH_KEY - EditorInfo.IME_ACTION_SEND -> KeyboardIconsSet.NAME_SEND_KEY - EditorInfo.IME_ACTION_NEXT -> KeyboardIconsSet.NAME_NEXT_KEY - EditorInfo.IME_ACTION_DONE -> KeyboardIconsSet.NAME_DONE_KEY - EditorInfo.IME_ACTION_PREVIOUS -> KeyboardIconsSet.NAME_PREVIOUS_KEY - InputTypeUtils.IME_ACTION_CUSTOM_LABEL -> return params.mId.mCustomActionLabel - else -> return "!icon/enter_key" - } - val replacement = iconName.replaceIconWithLabelIfNoDrawable() - return if (iconName == replacement) // i.e. icon exists - "!icon/$iconName" - else - replacement - } - - private fun getActionKeyCode() = - if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) - "!code/key_shift_enter" - else "!code/key_enter" - - private fun getActionKeyPopupKeys(): Collection? { - val action = params.mId.imeAction() - val navigatePrev = params.mId.navigatePrevious() - val navigateNext = params.mId.navigateNext() - return when { - params.mId.passwordInput() -> when { - navigatePrev && action == EditorInfo.IME_ACTION_NEXT -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS) - action == EditorInfo.IME_ACTION_NEXT -> null - navigateNext && action == EditorInfo.IME_ACTION_PREVIOUS -> createPopupKeys(POPUP_EYS_NAVIGATE_NEXT) - action == EditorInfo.IME_ACTION_PREVIOUS -> null - navigateNext && navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS_NEXT) - navigateNext -> createPopupKeys(POPUP_EYS_NAVIGATE_NEXT) - navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS) - else -> null - } - // could change definition of numbers to query a range, or have a pre-defined list, but not that crucial - params.mId.isNumberLayout || params.mId.mMode in listOf(KeyboardId.MODE_EMAIL, KeyboardId.MODE_DATE, KeyboardId.MODE_TIME, KeyboardId.MODE_DATETIME) -> when { - action == EditorInfo.IME_ACTION_NEXT && navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS) - action == EditorInfo.IME_ACTION_NEXT -> null - action == EditorInfo.IME_ACTION_PREVIOUS && navigateNext -> createPopupKeys(POPUP_EYS_NAVIGATE_NEXT) - action == EditorInfo.IME_ACTION_PREVIOUS -> null - navigateNext && navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS_NEXT) - navigateNext -> createPopupKeys(POPUP_EYS_NAVIGATE_NEXT) - navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_PREVIOUS) - else -> null - } - action == EditorInfo.IME_ACTION_NEXT && navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS) - action == EditorInfo.IME_ACTION_NEXT -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI) - action == EditorInfo.IME_ACTION_PREVIOUS && navigateNext -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI_NEXT) - action == EditorInfo.IME_ACTION_PREVIOUS -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI) - navigateNext && navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS_NEXT) - navigateNext -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI_NEXT) - navigatePrev -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS) - else -> createPopupKeys(POPUP_EYS_NAVIGATE_EMOJI) - } - } - - private fun createPopupKeys(popupKeysDef: String): List { - val popupKeys = mutableListOf() - for (popupKey in popupKeysDef.split(",")) { - val iconPrefixRemoved = popupKey.substringAfter("!icon/") - if (iconPrefixRemoved == popupKey) { // i.e. there is no !icon/ - popupKeys.add(popupKey) - continue - } - val iconName = iconPrefixRemoved.substringBefore("|") - val replacementText = iconName.replaceIconWithLabelIfNoDrawable() - if (replacementText == iconName) { // i.e. we have the drawable - popupKeys.add(popupKey) - } else { - popupKeys.add(Key.POPUP_KEYS_HAS_LABELS) - popupKeys.add("$replacementText|${iconPrefixRemoved.substringAfter("|")}") - } - } - // remove emoji shortcut on enter in tablet mode (like original, because bottom row always has an emoji key) - // (probably not necessary, but whatever) - if (Settings.getInstance().isTablet && popupKeys.remove("!icon/emoji_action_key|!code/key_emoji")) { - val i = popupKeys.indexOfFirst { it.startsWith(Key.POPUP_KEYS_FIXED_COLUMN_ORDER) } - if (i > -1) { - val n = popupKeys[i].substringAfter(Key.POPUP_KEYS_FIXED_COLUMN_ORDER).toIntOrNull() - if (n != null) - popupKeys[i] = popupKeys[i].replace(n.toString(), (n - 1).toString()) - } - } - return popupKeys - } - - private fun String.replaceIconWithLabelIfNoDrawable(): String { - if (params.mIconsSet.getIconDrawable(this) != null) return this - if (params.mId.mWidth == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_WIDTH - && params.mId.mHeight == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT - && !params.mId.mSubtype.hasExtraValue(Constants.Subtype.ExtraValue.EMOJI_CAPABLE) - ) - // fake keyboard that is used by spell checker (for key coordinates), but not shown to the user - // often this doesn't have any icons loaded, and there is no need to bother with this - return this - val id = context.resources.getIdentifier("label_$this", "string", context.packageName) - if (id == 0) { - Log.w(TAG, "no resource for label $this in ${params.mId}") - return this - } - return getInLocale(id) - } - - private fun getInLocale(@StringRes id: Int): String { - // todo: hi-Latn strings instead of this workaround? - val locale = if (params.mId.locale.toLanguageTag() == "hi-Latn") "en_IN".constructLocale() - else params.mId.locale - return runInLocale(context, locale) { it.getString(id) } - } - companion object { private const val TAG = "KeyboardParser" @@ -526,15 +382,6 @@ fun String.rtlLabel(params: KeyboardParams): String { } } -// could make arrays right away, but they need to be copied anyway as popupKeys arrays are modified when creating KeyParams -private const val POPUP_EYS_NAVIGATE_PREVIOUS = "!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard" -private const val POPUP_EYS_NAVIGATE_NEXT = "!icon/clipboard_action_key|!code/key_clipboard,!icon/next_key|!code/key_action_next" -private const val POPUP_EYS_NAVIGATE_PREVIOUS_NEXT = "!fixedColumnOrder!3,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/next_key|!code/key_action_next" -private const val POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS = "!fixedColumnOrder!3,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji" -private const val POPUP_EYS_NAVIGATE_EMOJI = "!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji" -private const val POPUP_EYS_NAVIGATE_EMOJI_NEXT = "!fixedColumnOrder!3,!needsDividers!,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji,!icon/next_key|!code/key_action_next" -private const val POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS_NEXT = "!fixedColumnOrder!4,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji,!icon/next_key|!code/key_action_next" - const val LAYOUT_SYMBOLS = "symbols" const val LAYOUT_SYMBOLS_SHIFTED = "symbols_shifted" const val LAYOUT_SYMBOLS_ARABIC = "symbols_arabic" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt index 77e7f997e..4082a47cd 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/floris/TextKeyData.kt @@ -5,6 +5,7 @@ */ package helium314.keyboard.keyboard.internal.keyboard_parser.floris +import android.view.inputmethod.EditorInfo import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -17,8 +18,12 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.check import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyLabel.convertFlorisLabel import helium314.keyboard.keyboard.internal.keyboard_parser.rtlLabel import helium314.keyboard.latin.common.Constants +import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.StringUtils import helium314.keyboard.latin.settings.Settings +import helium314.keyboard.latin.spellcheck.AndroidSpellCheckerService +import helium314.keyboard.latin.utils.InputTypeUtils +import helium314.keyboard.latin.utils.Log // taken from FlorisBoard, small modifications (see also KeyData) // internal keys removed (currently no plan to support them) @@ -84,6 +89,7 @@ sealed interface KeyData : AbstractKeyData { KeyboardId.ELEMENT_SYMBOLS -> params.mLocaleKeyboardInfos.getShiftSymbolLabel(Settings.getInstance().isTablet) KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED, KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY_SHIFTED}" + else -> "!icon/${KeyboardIconsSet.NAME_SHIFT_KEY}" } @@ -138,6 +144,144 @@ sealed interface KeyData : AbstractKeyData { } return popupKeys } + + private fun String.resolveStringLabel(params: KeyboardParams): String { + if (length < 9 || !startsWith("!string/")) return this + val id = Settings.getInstance().getStringResIdByName(substringAfter("!string/")) + if (id == 0) return this + return getStringInLocale(id, params) + } + + private fun getStringInLocale(id: Int, params: KeyboardParams): String { + // todo: hi-Latn strings instead of this workaround? + val locale = if (params.mId.locale.toLanguageTag() == "hi-Latn") "en_IN".constructLocale() + else params.mId.locale + return Settings.getInstance().getInLocale(id, locale) + } + + // action key stuff below + + // todo (later): should this be handled with metaState? but metaState shift would require LOTS of changes... + private fun getActionKeyCode(params: KeyboardParams) = + if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) + "!code/key_shift_enter" + else "!code/key_enter" + + private fun getActionKeyLabel(params: KeyboardParams): String { + if (params.mId.isMultiLine && (params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED || params.mId.mElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)) + return "!icon/enter_key" + val iconName = when (params.mId.imeAction()) { + EditorInfo.IME_ACTION_GO -> KeyboardIconsSet.NAME_GO_KEY + EditorInfo.IME_ACTION_SEARCH -> KeyboardIconsSet.NAME_SEARCH_KEY + EditorInfo.IME_ACTION_SEND -> KeyboardIconsSet.NAME_SEND_KEY + EditorInfo.IME_ACTION_NEXT -> KeyboardIconsSet.NAME_NEXT_KEY + EditorInfo.IME_ACTION_DONE -> KeyboardIconsSet.NAME_DONE_KEY + EditorInfo.IME_ACTION_PREVIOUS -> KeyboardIconsSet.NAME_PREVIOUS_KEY + InputTypeUtils.IME_ACTION_CUSTOM_LABEL -> return params.mId.mCustomActionLabel + else -> return "!icon/enter_key" + } + val replacement = iconName.replaceIconWithLabelIfNoDrawable(params) + return if (iconName == replacement) // i.e. icon exists + "!icon/$iconName" + else + replacement + } + + private fun getActionKeyPopupKeys(params: KeyboardParams): SimplePopups? = + getActionKeyPopupKeyString(params.mId)?.let { createActionPopupKeys(it, params) } + + private fun getActionKeyPopupKeyString(keyboardId: KeyboardId): String? { + val action = keyboardId.imeAction() + val navigatePrev = keyboardId.navigatePrevious() + val navigateNext = keyboardId.navigateNext() + return when { + keyboardId.passwordInput() -> when { + navigatePrev && action == EditorInfo.IME_ACTION_NEXT -> POPUP_EYS_NAVIGATE_PREVIOUS + action == EditorInfo.IME_ACTION_NEXT -> null + navigateNext && action == EditorInfo.IME_ACTION_PREVIOUS -> POPUP_EYS_NAVIGATE_NEXT + action == EditorInfo.IME_ACTION_PREVIOUS -> null + navigateNext && navigatePrev -> POPUP_EYS_NAVIGATE_PREVIOUS_NEXT + navigateNext -> POPUP_EYS_NAVIGATE_NEXT + navigatePrev -> POPUP_EYS_NAVIGATE_PREVIOUS + else -> null + } + // could change definition of numbers to query a range, or have a pre-defined list, but not that crucial + keyboardId.isNumberLayout || keyboardId.mMode in listOf(KeyboardId.MODE_EMAIL, KeyboardId.MODE_DATE, KeyboardId.MODE_TIME, KeyboardId.MODE_DATETIME) -> when { + action == EditorInfo.IME_ACTION_NEXT && navigatePrev -> POPUP_EYS_NAVIGATE_PREVIOUS + action == EditorInfo.IME_ACTION_NEXT -> null + action == EditorInfo.IME_ACTION_PREVIOUS && navigateNext -> POPUP_EYS_NAVIGATE_NEXT + action == EditorInfo.IME_ACTION_PREVIOUS -> null + navigateNext && navigatePrev -> POPUP_EYS_NAVIGATE_PREVIOUS_NEXT + navigateNext -> POPUP_EYS_NAVIGATE_NEXT + navigatePrev -> POPUP_EYS_NAVIGATE_PREVIOUS + else -> null + } + action == EditorInfo.IME_ACTION_NEXT && navigatePrev -> POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS + action == EditorInfo.IME_ACTION_NEXT -> POPUP_EYS_NAVIGATE_EMOJI + action == EditorInfo.IME_ACTION_PREVIOUS && navigateNext -> POPUP_EYS_NAVIGATE_EMOJI_NEXT + action == EditorInfo.IME_ACTION_PREVIOUS -> POPUP_EYS_NAVIGATE_EMOJI + navigateNext && navigatePrev -> POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS_NEXT + navigateNext -> POPUP_EYS_NAVIGATE_EMOJI_NEXT + navigatePrev -> POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS + else -> POPUP_EYS_NAVIGATE_EMOJI + } + } + + private fun createActionPopupKeys(popupKeysDef: String, params: KeyboardParams): SimplePopups { + val popupKeys = mutableListOf() + for (popupKey in popupKeysDef.split(",")) { + val iconPrefixRemoved = popupKey.substringAfter("!icon/") + if (iconPrefixRemoved == popupKey) { // i.e. there is no !icon/ + popupKeys.add(popupKey) + continue + } + val iconName = iconPrefixRemoved.substringBefore("|") + val replacementText = iconName.replaceIconWithLabelIfNoDrawable(params) + if (replacementText == iconName) { // i.e. we have the drawable + popupKeys.add(popupKey) + } else { + popupKeys.add(Key.POPUP_KEYS_HAS_LABELS) + popupKeys.add("$replacementText|${iconPrefixRemoved.substringAfter("|")}") + } + } + // remove emoji shortcut on enter in tablet mode (like original, because bottom row always has an emoji key) + // (probably not necessary, but whatever) + if (Settings.getInstance().isTablet && popupKeys.remove("!icon/emoji_action_key|!code/key_emoji")) { + val i = popupKeys.indexOfFirst { it.startsWith(Key.POPUP_KEYS_FIXED_COLUMN_ORDER) } + if (i > -1) { + val n = popupKeys[i].substringAfter(Key.POPUP_KEYS_FIXED_COLUMN_ORDER).toIntOrNull() + if (n != null) + popupKeys[i] = popupKeys[i].replace(n.toString(), (n - 1).toString()) + } + } + return SimplePopups(popupKeys) + } + + private fun String.replaceIconWithLabelIfNoDrawable(params: KeyboardParams): String { + if (params.mIconsSet.getIconDrawable(this) != null) return this + if (params.mId.mWidth == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_WIDTH + && params.mId.mHeight == AndroidSpellCheckerService.SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT + && !params.mId.mSubtype.hasExtraValue(Constants.Subtype.ExtraValue.EMOJI_CAPABLE) + ) + // fake keyboard that is used by spell checker (for key coordinates), but not shown to the user + // often this doesn't have any icons loaded, and there is no need to bother with this + return this + val id = Settings.getInstance().getStringResIdByName("label_$this") + if (id == 0) { + Log.w("TextKeyData", "no resource for label $this in ${params.mId}") + return this + } + return getStringInLocale(id, params) + } + + // could make arrays right away, but they need to be copied anyway as popupKeys arrays are modified when creating KeyParams + private const val POPUP_EYS_NAVIGATE_PREVIOUS = "!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard" + private const val POPUP_EYS_NAVIGATE_NEXT = "!icon/clipboard_action_key|!code/key_clipboard,!icon/next_key|!code/key_action_next" + private const val POPUP_EYS_NAVIGATE_PREVIOUS_NEXT = "!fixedColumnOrder!3,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/next_key|!code/key_action_next" + private const val POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS = "!fixedColumnOrder!3,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji" + private const val POPUP_EYS_NAVIGATE_EMOJI = "!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji" + private const val POPUP_EYS_NAVIGATE_EMOJI_NEXT = "!fixedColumnOrder!3,!needsDividers!,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji,!icon/next_key|!code/key_action_next" + private const val POPUP_EYS_NAVIGATE_EMOJI_PREVIOUS_NEXT = "!fixedColumnOrder!4,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/clipboard_action_key|!code/key_clipboard,!icon/emoji_action_key|!code/key_emoji,!icon/next_key|!code/key_action_next" } // make it non-nullable for simplicity, and to reflect current implementations @@ -145,7 +289,7 @@ sealed interface KeyData : AbstractKeyData { require(groupId <= GROUP_ENTER) { "only groups up to GROUP_ENTER are supported" } require(label.isNotEmpty() || type == KeyType.PLACEHOLDER || code != KeyCode.UNSPECIFIED) { "non-placeholder key has no code and no label" } require(width >= 0f || width == -1f) { "illegal width $width" } - val newLabel = label.convertFlorisLabel() + val newLabel = label.convertFlorisLabel().resolveStringLabel(params) val newCode = code.checkAndConvertCode() val newLabelFlags = if (labelFlags == 0 && params.mId.isNumberLayout) { if (type == KeyType.NUMERIC) { @@ -224,7 +368,7 @@ sealed interface KeyData : AbstractKeyData { ) } else { Key.KeyParams( - newLabel.rtlLabel(params), // todo (when supported): convert special labels to keySpec + newLabel.rtlLabel(params), params, newWidth, newLabelFlags, @@ -251,7 +395,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL, KeyLabel.ALPHA, KeyLabel.COMMA, KeyLabel.PERIOD, KeyLabel.DELETE, KeyLabel.EMOJI, KeyLabel.COM, KeyLabel.LANGUAGE_SWITCH, KeyLabel.NUMPAD -> return Key.BACKGROUND_TYPE_FUNCTIONAL KeyLabel.SPACE, KeyLabel.ZWNJ -> return Key.BACKGROUND_TYPE_SPACEBAR -// KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION + KeyLabel.ACTION -> return Key.BACKGROUND_TYPE_ACTION KeyLabel.SHIFT -> return getShiftBackground(params) } if (type == KeyType.PLACEHOLDER) return Key.BACKGROUND_TYPE_EMPTY @@ -280,7 +424,7 @@ sealed interface KeyData : AbstractKeyData { KeyLabel.COMMA -> params.mLocaleKeyboardInfos.labelComma KeyLabel.PERIOD -> getPeriodLabel(params) KeyLabel.SPACE -> getSpaceLabel(params) -// KeyLabel.ACTION -> "${getActionKeyLabel(params)}|${getActionKeyCode(params)}" would need context + KeyLabel.ACTION -> "${getActionKeyLabel(params)}|${getActionKeyCode(params)}" KeyLabel.DELETE -> "!icon/delete_key|!code/key_delete" KeyLabel.SHIFT -> "${getShiftLabel(params)}|!code/key_shift" KeyLabel.EMOJI -> "!icon/emoji_normal_key|!code/key_emoji" @@ -308,16 +452,16 @@ sealed interface KeyData : AbstractKeyData { } } - // todo (later): add explanations / reasoning, often this is just taken from conversion from AOSP layouts + // todo (later): add explanations / reasoning, often this is just taken from conversion from OpenBoard / AOSP layouts private fun getAdditionalLabelFlags(params: KeyboardParams): Int { return when (label) { KeyLabel.ALPHA, KeyLabel.SYMBOL_ALPHA, KeyLabel.SYMBOL -> Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR KeyLabel.PERIOD, KeyLabel.COMMA -> Key.LABEL_FLAGS_HAS_POPUP_HINT // todo: period also has defaultLabelFlags -> when is this relevant? -// KeyLabel.ACTION -> { -// Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or -// Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or -// Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) -// } + KeyLabel.ACTION -> { + Key.LABEL_FLAGS_PRESERVE_CASE or Key.LABEL_FLAGS_AUTO_X_SCALE or + Key.LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO or Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR or + Key.LABEL_FLAGS_HAS_POPUP_HINT or KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) + } KeyLabel.SPACE -> if (params.mId.isNumberLayout) Key.LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM else 0 KeyLabel.SHIFT -> Key.LABEL_FLAGS_PRESERVE_CASE or if (!params.mId.isAlphabetKeyboard) Key.LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR else 0 KeyLabel.EMOJI -> KeyboardTheme.getThemeActionAndEmojiKeyLabelFlags(params.mThemeId) @@ -331,11 +475,11 @@ sealed interface KeyData : AbstractKeyData { private fun getAdditionalPopupKeys(params: KeyboardParams): PopupSet? { if (groupId == GROUP_COMMA) return SimplePopups(getCommaPopupKeys(params)) if (groupId == GROUP_PERIOD) return SimplePopups(getPunctuationPopupKeys(params)) -// if (groupId == GROUP_ENTER) return getActionKeyPopupKeys(params)?.let { SimplePopups(it) } + if (groupId == GROUP_ENTER) return getActionKeyPopupKeys(params) return when (label) { KeyLabel.COMMA -> SimplePopups(getCommaPopupKeys(params)) KeyLabel.PERIOD -> SimplePopups(getPunctuationPopupKeys(params)) -// KeyLabel.ACTION -> getActionKeyPopupKeys(params)?.let { SimplePopups(it) } + KeyLabel.ACTION -> getActionKeyPopupKeys(params) KeyLabel.SHIFT -> { if (params.mId.isAlphabetKeyboard) SimplePopups( listOf( diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 9f7b2f97a..4ba7601a6 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -23,6 +23,7 @@ import android.view.Gravity; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; @@ -692,4 +693,12 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public boolean isTablet() { return mContext.getResources().getInteger(R.integer.config_screen_metrics) >= 3; } + + public int getStringResIdByName(final String name) { + return mContext.getResources().getIdentifier(name, "string", mContext.getPackageName()); + } + + public String getInLocale(@StringRes final int resId, final Locale locale) { + return RunInLocaleKt.runInLocale(mContext, locale, (ctx) -> ctx.getString(resId)); + } }