From ed776c38043a87f101890376cd1715e2324e1790 Mon Sep 17 00:00:00 2001 From: Helium314 Date: Tue, 14 May 2024 23:31:55 +0200 Subject: [PATCH] overhaul style of parsing layouts and add a cache --- .../keyboard/keyboard/KeyboardLayoutSet.java | 2 + .../keyboard/internal/KeyboardBuilder.kt | 2 +- .../keyboard_parser/JsonKeyboardParser.kt | 72 ------- .../keyboard_parser/KeyboardParser.kt | 109 ++-------- .../keyboard_parser/RawKeyboardParser.kt | 199 ++++++++++++++++++ .../keyboard_parser/SimpleKeyboardParser.kt | 48 ----- .../main/java/helium314/keyboard/latin/App.kt | 5 +- .../settings/AdvancedSettingsFragment.kt | 18 +- .../keyboard/latin/settings/Settings.java | 17 -- .../keyboard/latin/utils/CustomLayoutUtils.kt | 23 +- .../keyboard/latin/utils/SubtypeSettings.kt | 2 +- .../helium314/keyboard/KeyboardParserTest.kt | 21 +- 12 files changed, 256 insertions(+), 262 deletions(-) delete mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt create mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt delete mode 100644 app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt diff --git a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java index 397b063a9..e77b976f3 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java +++ b/app/src/main/java/helium314/keyboard/keyboard/KeyboardLayoutSet.java @@ -17,6 +17,7 @@ import helium314.keyboard.keyboard.internal.KeyboardParams; import helium314.keyboard.keyboard.internal.UniqueKeysCache; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfos; import helium314.keyboard.keyboard.internal.keyboard_parser.LocaleKeyboardInfosKt; +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser; import helium314.keyboard.latin.RichInputMethodSubtype; import helium314.keyboard.latin.utils.InputTypeUtils; import helium314.keyboard.latin.utils.Log; @@ -99,6 +100,7 @@ public final class KeyboardLayoutSet { private static void clearKeyboardCache() { sKeyboardCache.clear(); sUniqueKeysCache.clear(); + RawKeyboardParser.INSTANCE.clearCache(); } KeyboardLayoutSet(final Context context, @NonNull final Params params) { diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt index b3fd54887..855c31d4b 100644 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/KeyboardBuilder.kt @@ -59,7 +59,7 @@ open class KeyboardBuilder(protected val mContext: Context, mParams.mPopupKeyTypes.addAll(sv.mPopupKeyTypes) // add label source only if popup key type enabled sv.mPopupKeyLabelSources.forEach { if (it in sv.mPopupKeyTypes) mParams.mPopupKeyLabelSources.add(it) } - keysInRows = KeyboardParser.parseLayout(mParams, mContext) + keysInRows = KeyboardParser(mParams, mContext).parseLayout() determineAbsoluteValues() } catch (e: Exception) { Log.e(TAG, "error parsing layout $id ${id.mElementId}", e) diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt deleted file mode 100644 index bb7f0d563..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/JsonKeyboardParser.kt +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -package helium314.keyboard.keyboard.internal.keyboard_parser - -import android.content.Context -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic -import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CaseSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KanaSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.ShiftStateSelector -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector - -/** - * Parser for json layout files as used in FlorisBoard, see floris directory for classes taken from FlorisBoard. - * Some differences to the FlorisBoard keys: - * (currently) only normal keys supported - * if label or code are missing one is created from the other - * auto_text_key ignored (i.e. interpreted like the default TextKey) - * codes of multi_text_key not used, only the label - * (currently) popups is always read to [number, main, relevant] layoutPopupKeys, no choice of which to use or which hint is provided - */ -class JsonKeyboardParser(private val params: KeyboardParams, context: Context) : KeyboardParser(params, context) { - - override fun parseCoreLayout(layoutContent: String): MutableList> { - val florisKeyData: List> = florisJsonConfig.decodeFromString(layoutContent) - // initially 200 ms parse (debug build on S4 mini) - // after a few parses it's optimized and 20-30 ms - // whole load is 50-70 ms vs 30-55 with simple parser -> it's ok - return florisKeyData.mapTo(mutableListOf()) { it.mapNotNull { it.compute(params) } } - } - -} - -/* - * Copyright (C) 2021 Patrick Goldinger - * modified - * SPDX-License-Identifier: Apache-2.0 - */ -private val florisJsonConfig = Json { - classDiscriminator = "$" - encodeDefaults = true - ignoreUnknownKeys = true - isLenient = true - serializersModule = SerializersModule { - polymorphic(AbstractKeyData::class) { - subclass(TextKeyData::class, TextKeyData.serializer()) - subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) - subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) - subclass(CaseSelector::class, CaseSelector.serializer()) - subclass(ShiftStateSelector::class, ShiftStateSelector.serializer()) - subclass(VariationSelector::class, VariationSelector.serializer()) - subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer()) - subclass(CharWidthSelector::class, CharWidthSelector.serializer()) - subclass(KanaSelector::class, KanaSelector.serializer()) - defaultDeserializer { TextKeyData.serializer() } - } - polymorphic(KeyData::class) { - subclass(TextKeyData::class, TextKeyData.serializer()) - subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) - subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) - defaultDeserializer { TextKeyData.serializer() } - } - } -} 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 51062d742..5b9d15809 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 @@ -24,19 +24,16 @@ 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.CUSTOM_LAYOUT_PREFIX 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.getLayoutFile 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 -import java.io.File /** * Abstract parser class that handles creation of keyboard from [KeyData] arranged in rows, @@ -47,7 +44,7 @@ import java.io.File * By default, all normal keys have the same width and flags, which may cause issues with the * requirements of certain non-latin languages. */ -abstract class KeyboardParser(private val params: KeyboardParams, private val context: Context) { +class KeyboardParser(private val params: KeyboardParams, private val context: Context) { private val infos = layoutInfos(params) private val defaultLabelFlags = when { params.mId.isAlphabetKeyboard -> params.mLocaleKeyboardInfos.labelFlags @@ -57,10 +54,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else -> 0 } - abstract fun parseCoreLayout(layoutContent: String): MutableList> - - // this thing does too much... make it more understandable after everything is implemented - fun parseLayoutString(layoutContent: String): ArrayList> { + fun parseLayout(): ArrayList> { params.readAttributes(context, null) params.mProximityCharsCorrectionEnabled = infos.enableProximityCharsCorrection if (infos.touchPositionCorrectionData == null) // need to set correctly, as it's not properly done in readAttributes with attr = null @@ -68,7 +62,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co else params.mTouchPositionCorrection.load(context.resources.getStringArray(infos.touchPositionCorrectionData)) - val baseKeys = parseCoreLayout(layoutContent) + val baseKeys = RawKeyboardParser.parseLayout(params, context) val keysInRows = createRows(baseKeys) // rescale height if we have anything but the usual 4 rows val heightRescale = if (keysInRows.size != 4) 4f / keysInRows.size else 1f @@ -79,26 +73,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co return keysInRows } - // this should be ready for customizable functional layouts, but needs cleanup - // todo (later): remove this as part of adding a cache for parsed layouts - private fun getFunctionalKeyLayoutText(): String { - if (params.mId.isNumberLayout) return "[]" // empty list - val layouts = Settings.getLayoutsDir(context).list() ?: emptyArray() - if (params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED) { - if ("functional_keys_symbols_shifted.json" in layouts) - return getLayoutFile("functional_keys_symbols_shifted.json", context).readText() - } - if (!params.mId.isAlphabetKeyboard) { - if ("functional_keys_symbols.json" in layouts) - return getLayoutFile("functional_keys_symbols.json", context).readText() - } - if ("functional_keys.json" in layouts) - return getLayoutFile("functional_keys.json", context).readText() - val fileName = if (Settings.getInstance().isTablet) "functional_keys_tablet.json" else "functional_keys.json" - return context.readAssetsLayoutFile(fileName) - } - - private fun createRows(baseKeys: MutableList>): ArrayList> { + private fun createRows(baseKeys: MutableList>): ArrayList> { // add padding for number layouts in landscape mode (maybe do it some other way later) if (params.mId.isNumberLayout && params.mId.mElementId != KeyboardId.ELEMENT_NUMPAD && context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { @@ -112,14 +87,14 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co addSymbolPopupKeys(baseKeys) if (params.mId.isAlphaOrSymbolKeyboard && params.mId.mNumberRowEnabled) baseKeys.add(0, params.mLocaleKeyboardInfos.getNumberRow() - .map { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) + .mapTo(mutableListOf()) { it.copy(newLabelFlags = Key.LABEL_FLAGS_DISABLE_HINT_LABEL or defaultLabelFlags) }) - val allFunctionalKeys = JsonKeyboardParser(params, context).parseCoreLayout(getFunctionalKeyLayoutText()) + val allFunctionalKeys = RawKeyboardParser.parseLayout(params, context, true) adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys, baseKeys) if (allFunctionalKeys.none { it.singleOrNull()?.isKeyPlaceholder() == true }) // add a placeholder so splitAt does what we really want - allFunctionalKeys.add(0, listOf(TextKeyData(type = KeyType.PLACEHOLDER))) + allFunctionalKeys.add(0, mutableListOf(TextKeyData(type = KeyType.PLACEHOLDER))) val (functionalKeysTop, functionalKeysBottom) = allFunctionalKeys.splitAt { it.singleOrNull()?.isKeyPlaceholder() == true } @@ -235,8 +210,8 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co * does nothing if not isAlphaOrSymbolKeyboard or assumptions not met * adds an empty row to baseKeys, to have a baseKey row for the bottom functional row */ - private fun adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys: MutableList>, baseKeys: MutableList>) { - val functionalKeysBottom = allFunctionalKeys.lastOrNull()?.toMutableList() ?: return + private fun adjustBottomFunctionalRowAndBaseKeys(allFunctionalKeys: MutableList>, baseKeys: MutableList>) { + val functionalKeysBottom = allFunctionalKeys.lastOrNull() ?: return if (!params.mId.isAlphaOrSymbolKeyboard || functionalKeysBottom.isEmpty() || functionalKeysBottom.any { it.isKeyPlaceholder() }) return if (true /* Settings.getInstance().current.mSingleFunctionalLayout */) { // todo with the customizable functional layout @@ -287,8 +262,7 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co functionalKeysBottom.add(spaceIndex, key1) } } - allFunctionalKeys[allFunctionalKeys.lastIndex] = functionalKeysBottom - baseKeys.add(emptyList()) + baseKeys.add(mutableListOf()) } // ideally we would get all functional keys in a nice list of pairs from the start, but at least it works... @@ -331,12 +305,12 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { + private fun addNumberRowOrPopupKeys(baseKeys: MutableList>) { if (!params.mId.mNumberRowEnabled && params.mId.mElementId == KeyboardId.ELEMENT_SYMBOLS) { // replace first symbols row with number row, but use the labels as popupKeys val numberRow = params.mLocaleKeyboardInfos.getNumberRow() numberRow.forEachIndexed { index, keyData -> keyData.popup.symbol = baseKeys[0].getOrNull(index)?.label } - baseKeys[0] = numberRow + baseKeys[0] = numberRow.toMutableList() } else if (!params.mId.mNumberRowEnabled && params.mId.isAlphabetKeyboard && infos.numbersOnTopRow) { if (baseKeys[0].any { it.popup.main != null || !it.popup.relevant.isNullOrEmpty() } // first row of baseKeys has any layout popup key && params.mPopupKeyLabelSources.let { @@ -356,15 +330,9 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co } } - private fun addSymbolPopupKeys(baseKeys: MutableList>) { - val layoutName = getLayoutFileName(params, context, overrideElementId = KeyboardId.ELEMENT_SYMBOLS) - val layout = if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { - val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context) - else SimpleKeyboardParser(params, context, false) - parser.parseCoreLayout(getLayoutFile(layoutName, context).readText()) - } else { - SimpleKeyboardParser(params, context, false).parseCoreLayout(context.readAssetsLayoutFile("$layoutName.txt")) - } + private fun addSymbolPopupKeys(baseKeys: MutableList>) { + val layoutName = if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS + val layout = RawKeyboardParser.parseLayout(layoutName, params, context) layout.forEachIndexed { i, row -> val baseRow = baseKeys.getOrNull(i) ?: return@forEachIndexed row.forEachIndexed { j, key -> @@ -492,49 +460,6 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co companion object { private const val TAG = "KeyboardParser" - // todo: this is somewhat awkward and could be re-organized - // simple and json parser should just parse the core layout - // adding extra keys should be done in KeyboardParser - fun parseLayout(params: KeyboardParams, context: Context): ArrayList> { - val layoutName = getLayoutFileName(params, context) - if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { - val parser = if (layoutName.endsWith("json")) JsonKeyboardParser(params, context) - else SimpleKeyboardParser(params, context) - return parser.parseLayoutString(getLayoutFile(layoutName, context).readText()) - } - val layoutFileNames = context.assets.list("layouts")!! - if (layoutFileNames.contains("$layoutName.json")) { - return JsonKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.json")) - } - if (layoutFileNames.contains("$layoutName.txt")) { - return SimpleKeyboardParser(params, context).parseLayoutString(context.readAssetsLayoutFile("$layoutName.txt")) - } - throw IllegalStateException("can't parse layout $layoutName with id ${params.mId} and elementId ${params.mId.mElementId}") - } - - private fun Context.readAssetsLayoutFile(name: String) = assets.open("layouts${File.separator}$name").reader().readText() - - private fun getLayoutFileName(params: KeyboardParams, context: Context, overrideElementId: Int? = null): String { - var checkForCustom = true - val layoutName = when (overrideElementId ?: params.mId.mElementId) { - KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS - KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LAYOUT_SYMBOLS_SHIFTED - KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) - LAYOUT_NUMPAD_LANDSCAPE - else - LAYOUT_NUMPAD - KeyboardId.ELEMENT_NUMBER -> LAYOUT_NUMBER - KeyboardId.ELEMENT_PHONE -> LAYOUT_PHONE - KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS - else -> { - checkForCustom = false // "custom" is already in keyboardLayoutSetName - params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") - } - } - return if (checkForCustom) Settings.readLayoutName(layoutName, context) - else layoutName - } - // todo: // layoutInfos should be stored in method.xml (imeSubtypeExtraValue) // or somewhere else... some replacement for keyboard_layout_set xml maybe @@ -619,3 +544,7 @@ const val LAYOUT_NUMPAD_LANDSCAPE = "numpad_landscape" const val LAYOUT_NUMBER = "number" const val LAYOUT_PHONE = "phone" const val LAYOUT_PHONE_SYMBOLS = "phone_symbols" +const val FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED = "functional_keys_symbols_shifted" +const val FUNCTIONAL_LAYOUT_SYMBOLS = "functional_keys_symbols" +const val FUNCTIONAL_LAYOUT = "functional_keys" +const val FUNCTIONAL_LAYOUT_TABLET = "functional_keys_tablet" diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt new file mode 100644 index 000000000..3053c488d --- /dev/null +++ b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/RawKeyboardParser.kt @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-3.0-only +package helium314.keyboard.keyboard.internal.keyboard_parser + +import android.content.Context +import android.content.res.Configuration +import helium314.keyboard.keyboard.KeyboardId +import helium314.keyboard.keyboard.internal.KeyboardParams +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AbstractKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CaseSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KanaSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.ShiftStateSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.TextKeyData +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSelector +import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey +import helium314.keyboard.latin.common.splitOnWhitespace +import helium314.keyboard.latin.settings.Settings +import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX +import helium314.keyboard.latin.utils.ScriptUtils +import helium314.keyboard.latin.utils.ScriptUtils.script +import helium314.keyboard.latin.utils.getCustomLayoutFile +import helium314.keyboard.latin.utils.getCustomLayoutsDir +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import java.io.File + +object RawKeyboardParser { + private val rawLayoutCache = hashMapOf MutableList>>() + + val symbolAndNumberLayouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, + LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS) + + // todo: cache is by layout name, this is inefficient for functional keys by default + fun clearCache() = rawLayoutCache.clear() + + fun parseLayout(params: KeyboardParams, context: Context, isFunctional: Boolean = false): MutableList> { + val layoutName = if (isFunctional) { + if (!params.mId.isAlphaOrSymbolKeyboard) return mutableListOf(mutableListOf()) + else getFunctionalLayoutName(params) + } else { + getLayoutName(params, context) + } + return rawLayoutCache.getOrPut(layoutName) { + createCacheLambda(layoutName, context) + }(params) + } + + fun parseLayout(layoutName: String, params: KeyboardParams, context: Context): MutableList> { + return rawLayoutCache.getOrPut(layoutName) { + createCacheLambda(layoutName, context) + }(params) + } + + /** + * Parse for json layout files as used in FlorisBoard, see floris directory for classes taken from FlorisBoard. + * Some differences to the FlorisBoard keys: + * (currently) only normal keys supported + * if label or code are missing one is created from the other + * auto_text_key ignored (i.e. interpreted like the default TextKey) + * codes of multi_text_key not used, only the label + * (currently) popups is always read to [number, main, relevant] layoutPopupKeys, no choice of which to use or which hint is provided + */ + fun parseJsonString(layoutText: String): List> = florisJsonConfig.decodeFromString(layoutText) + + /** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */ + fun parseSimpleString(layoutText: String): List> { + val rowStrings = layoutText.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()).filter { it.isNotBlank() } + return rowStrings.map { row -> + row.split("\n").mapNotNull { parseKey(it) } + } + } + + private fun parseKey(key: String): KeyData? { + if (key.isBlank()) return null + val split = key.splitOnWhitespace() + return if (split.size == 1) split.first().toTextKey() + else split.first().toTextKey(split.drop(1)) + } + + private fun createCacheLambda(layoutName: String, context: Context): (KeyboardParams) -> MutableList> { + val layoutFileName = getLayoutFileName(layoutName, context) + val layoutText = if (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) getCustomLayoutFile(layoutFileName, context).readText() + else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() } + if (layoutFileName.endsWith(".json")) { + val florisKeyData = parseJsonString(layoutText) + return { params -> + florisKeyData.mapTo(mutableListOf()) { row -> + row.mapNotNullTo(mutableListOf()) { it.compute(params) } + } + } + } else { + val simpleKeyData = parseSimpleString(layoutText) + return { params -> + simpleKeyData.mapIndexedTo(mutableListOf()) { i, row -> + val newRow = row.toMutableList() + val extraKeys = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") && params.mId.isAlphabetKeyboard + if (extraKeys) + params.mLocaleKeyboardInfos.getExtraKeys(i+1)?.let { newRow.addAll(it) } + println("${newRow.size}: ${newRow.map { it.label }}") + newRow + } + } + } + } + + private fun getLayoutName(params: KeyboardParams, context: Context) = when (params.mId.mElementId) { + KeyboardId.ELEMENT_SYMBOLS -> if (params.mId.locale.script() == ScriptUtils.SCRIPT_ARABIC) LAYOUT_SYMBOLS_ARABIC else LAYOUT_SYMBOLS + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> LAYOUT_SYMBOLS_SHIFTED + KeyboardId.ELEMENT_NUMPAD -> if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) + LAYOUT_NUMPAD_LANDSCAPE + else + LAYOUT_NUMPAD + KeyboardId.ELEMENT_NUMBER -> LAYOUT_NUMBER + KeyboardId.ELEMENT_PHONE -> LAYOUT_PHONE + KeyboardId.ELEMENT_PHONE_SYMBOLS -> LAYOUT_PHONE_SYMBOLS + else -> params.mId.mSubtype.keyboardLayoutSetName.substringBeforeLast("+") + } + + private fun getFunctionalLayoutName(params: KeyboardParams) = when (params.mId.mElementId) { + KeyboardId.ELEMENT_SYMBOLS_SHIFTED -> FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED + KeyboardId.ELEMENT_SYMBOLS -> FUNCTIONAL_LAYOUT_SYMBOLS + else -> if (Settings.getInstance().isTablet) FUNCTIONAL_LAYOUT_TABLET else FUNCTIONAL_LAYOUT + } + + /** returns the file name matching the layout name, making sure the file exists (falling back to qwerty.txt) */ + private fun getLayoutFileName(layoutName: String, context: Context): String { + val customFiles = getCustomLayoutsDir(context).list() + if (layoutName.startsWith(CUSTOM_LAYOUT_PREFIX)) { + return if (customFiles?.contains(layoutName) == true) layoutName + else "qwerty.txt" // fallback + } + val assetsFiles by lazy { context.assets.list("layouts")!! } + if (layoutName.startsWith("functional")) { + // return custom match if we have one + val customMatch = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } + if (customMatch != null) return customMatch + if (layoutName == FUNCTIONAL_LAYOUT_SYMBOLS_SHIFTED) { + // no custom symbols shifted layout, try custom symbols layout + val customSymbols = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_SYMBOLS.") } + if (customSymbols != null) return customSymbols + } + // no custom symbols layout, try custom functional layout + if (Settings.getInstance().isTablet) { + val customTablet = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT_TABLET.") } + if (customTablet != null) return customTablet + } + val customFunctional = customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$FUNCTIONAL_LAYOUT.") } + if (customFunctional != null) return customFunctional + // no custom functional layout, use the default functional layout + return if (Settings.getInstance().isTablet) "$FUNCTIONAL_LAYOUT_TABLET.json" + else "$FUNCTIONAL_LAYOUT.json" + } + return if (layoutName in symbolAndNumberLayouts) { + customFiles?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.")} + ?: assetsFiles.first { it.startsWith(layoutName) } + } else { + // can't be custom layout, so it must be in assets + val searchName = layoutName.substringBeforeLast("+") // consider there are layouts ending in "+" for adding extra keys + assetsFiles.firstOrNull { it.startsWith(searchName) } ?: "qwerty.txt" // in case it was removed + } + } + + /* + * Copyright (C) 2021 Patrick Goldinger + * modified + * SPDX-License-Identifier: Apache-2.0 + */ + private val florisJsonConfig = Json { + classDiscriminator = "$" + encodeDefaults = true + ignoreUnknownKeys = true + isLenient = true + serializersModule = SerializersModule { + polymorphic(AbstractKeyData::class) { + subclass(TextKeyData::class, TextKeyData.serializer()) + subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) + subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) + subclass(CaseSelector::class, CaseSelector.serializer()) + subclass(ShiftStateSelector::class, ShiftStateSelector.serializer()) + subclass(VariationSelector::class, VariationSelector.serializer()) + subclass(LayoutDirectionSelector::class, LayoutDirectionSelector.serializer()) + subclass(CharWidthSelector::class, CharWidthSelector.serializer()) + subclass(KanaSelector::class, KanaSelector.serializer()) + defaultDeserializer { TextKeyData.serializer() } + } + polymorphic(KeyData::class) { + subclass(TextKeyData::class, TextKeyData.serializer()) + subclass(AutoTextKeyData::class, AutoTextKeyData.serializer()) + subclass(MultiTextKeyData::class, MultiTextKeyData.serializer()) + defaultDeserializer { TextKeyData.serializer() } + } + } + } +} diff --git a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt b/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt deleted file mode 100644 index cb730b1c0..000000000 --- a/app/src/main/java/helium314/keyboard/keyboard/internal/keyboard_parser/SimpleKeyboardParser.kt +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -package helium314.keyboard.keyboard.internal.keyboard_parser - -import android.content.Context -import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyData -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.toTextKey -import helium314.keyboard.latin.common.splitOnWhitespace - -/** - * Parser for simple layouts, defined only as rows of (normal) keys with popup keys. - * There may be a short "extra row" for the configurable keys in the bottom row. This is two keys - * for alphabet, 3 keys for symbols and 4 keys for shift symbols. Popup keys on period and comma get - * merged with defaults. - */ -class SimpleKeyboardParser( - private val params: KeyboardParams, - context: Context, - private val addExtraKeys: Boolean = params.mId.mSubtype.keyboardLayoutSetName.endsWith("+") && params.mId.isAlphabetKeyboard -) : KeyboardParser(params, context) { - override fun parseCoreLayout(layoutContent: String): MutableList> { - val rowStrings = layoutContent.replace("\r\n", "\n").split("\\n\\s*\\n".toRegex()) - return rowStrings.mapIndexedNotNullTo(mutableListOf()) { i, row -> - if (row.isBlank()) return@mapIndexedNotNullTo null - if (addExtraKeys) - getExtraKeys(i)?.let { parseRow(row) + it } ?: parseRow(row) - else - parseRow(row) - } - } - - private fun parseRow(row: String): List = - row.split("\n").mapNotNull { - if (it.isBlank()) null - else parseKey(it) - } - - private fun getExtraKeys(rowIndex: Int) = params.mLocaleKeyboardInfos.getExtraKeys(rowIndex + 1) - - private fun parseKey(key: String): KeyData { - val split = key.splitOnWhitespace() - return if (split.size == 1) - split.first().toTextKey() - else - split.first().toTextKey(split.drop(1)) - } - -} diff --git a/app/src/main/java/helium314/keyboard/latin/App.kt b/app/src/main/java/helium314/keyboard/latin/App.kt index 951f76387..a97a168a4 100644 --- a/app/src/main/java/helium314/keyboard/latin/App.kt +++ b/app/src/main/java/helium314/keyboard/latin/App.kt @@ -11,6 +11,7 @@ import helium314.keyboard.latin.settings.USER_DICTIONARY_SUFFIX import helium314.keyboard.latin.utils.CUSTOM_LAYOUT_PREFIX import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DictionaryInfoUtils +import helium314.keyboard.latin.utils.getCustomLayoutsDir import helium314.keyboard.latin.utils.upgradeToolbarPrefs import java.io.File @@ -50,7 +51,7 @@ fun checkVersionUpgrade(context: Context) { if (oldVersion == 0) // new install or restoring settings from old app name upgradesWhenComingFromOldAppName(context) if (oldVersion <= 1000) { // upgrade old custom layouts name - val layoutsDir = Settings.getLayoutsDir(context) + val layoutsDir = getCustomLayoutsDir(context) val oldShiftSymbolsFile = File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}shift_symbols") if (oldShiftSymbolsFile.exists()) { oldShiftSymbolsFile.renameTo(File(layoutsDir, "${CUSTOM_LAYOUT_PREFIX}symbols_shifted")) @@ -79,7 +80,7 @@ fun checkVersionUpgrade(context: Context) { private fun upgradesWhenComingFromOldAppName(context: Context) { // move layout files try { - val layoutsDir = Settings.getLayoutsDir(context) + val layoutsDir = getCustomLayoutsDir(context) File(context.filesDir, "layouts").listFiles()?.forEach { it.copyTo(File(layoutsDir, it.name), true) it.delete() diff --git a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt index 9176b9272..740af6f4c 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/AdvancedSettingsFragment.kt @@ -32,6 +32,7 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_PHONE_SYMBOLS import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_ARABIC import helium314.keyboard.keyboard.internal.keyboard_parser.LAYOUT_SYMBOLS_SHIFTED +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.latin.AudioAndHapticFeedbackManager import helium314.keyboard.latin.BuildConfig import helium314.keyboard.latin.R @@ -46,6 +47,7 @@ import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.ExecutorUtils import helium314.keyboard.latin.utils.JniUtils import helium314.keyboard.latin.utils.editCustomLayout +import helium314.keyboard.latin.utils.getCustomLayoutsDir import helium314.keyboard.latin.utils.getStringResourceOrName import helium314.keyboard.latin.utils.infoDialog import helium314.keyboard.latin.utils.reloadEnabledSubtypes @@ -157,27 +159,27 @@ class AdvancedSettingsFragment : SubScreenFragment() { } private fun showCustomizeLayoutsDialog() { - val layouts = listOf(LAYOUT_SYMBOLS, LAYOUT_SYMBOLS_SHIFTED, LAYOUT_SYMBOLS_ARABIC, LAYOUT_NUMBER, LAYOUT_NUMPAD, LAYOUT_NUMPAD_LANDSCAPE, LAYOUT_PHONE, LAYOUT_PHONE_SYMBOLS) - val layoutNames = layouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() + val layoutNames = RawKeyboardParser.symbolAndNumberLayouts.map { it.getStringResourceOrName("layout_", requireContext()) }.toTypedArray() AlertDialog.Builder(requireContext()) .setTitle(R.string.customize_symbols_number_layouts) .setItems(layoutNames) { di, i -> di.dismiss() - customizeLayout(layouts[i]) + customizeSymbolNumberLayout(RawKeyboardParser.symbolAndNumberLayouts[i]) } .setNegativeButton(android.R.string.cancel, null) .show() } - private fun customizeLayout(layout: String) { - val customLayoutName = Settings.readLayoutName(layout, context).takeIf { it.startsWith(CUSTOM_LAYOUT_PREFIX) } + private fun customizeSymbolNumberLayout(layoutName: String) { + val customLayoutName = getCustomLayoutsDir(requireContext()).list() + ?.firstOrNull { it.startsWith("$CUSTOM_LAYOUT_PREFIX$layoutName.") } val originalLayout = if (customLayoutName != null) null else { - requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layout.") } + requireContext().assets.list("layouts")?.firstOrNull { it.startsWith("$layoutName.") } ?.let { requireContext().assets.open("layouts" + File.separator + it).reader().readText() } } - val displayName = layout.getStringResourceOrName("layout_", requireContext()) - editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layout.txt", requireContext(), originalLayout, displayName) + val displayName = layoutName.getStringResourceOrName("layout_", requireContext()) + editCustomLayout(customLayoutName ?: "$CUSTOM_LAYOUT_PREFIX$layoutName.txt", requireContext(), originalLayout, displayName) } @SuppressLint("ApplySharedPref") 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 5d19431b8..9f7b2f97a 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -36,7 +36,6 @@ import helium314.keyboard.latin.common.Colors; import helium314.keyboard.latin.common.LocaleUtils; import helium314.keyboard.latin.utils.AdditionalSubtypeUtils; import helium314.keyboard.latin.utils.ColorUtilKt; -import helium314.keyboard.latin.utils.CustomLayoutUtilsKt; import helium314.keyboard.latin.utils.DeviceProtectedUtils; import helium314.keyboard.latin.utils.JniUtils; import helium314.keyboard.latin.utils.Log; @@ -548,22 +547,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang }; } - /** @return custom layout name if there is one for the given layout, else returns "layout" */ - public static String readLayoutName(final String layout, final Context context) { - String[] layouts = getLayoutsDir(context).list(); - if (layouts != null) { - for (String name : layouts) { - if (name.startsWith(CustomLayoutUtilsKt.CUSTOM_LAYOUT_PREFIX + layout + ".")) - return name; - } - } - return layout; - } - - public static File getLayoutsDir(final Context context) { - return new File(DeviceProtectedUtils.getFilesDir(context), "layouts"); - } - @Nullable public static Drawable readUserBackgroundImage(final Context context, final boolean night) { if (night && sCachedBackgroundNight != null) return sCachedBackgroundNight; if (!night && sCachedBackgroundDay != null) return sCachedBackgroundDay; diff --git a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt index 84479b637..05283a1f0 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/CustomLayoutUtils.kt @@ -13,9 +13,8 @@ import helium314.keyboard.keyboard.KeyboardId import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardSwitcher import helium314.keyboard.keyboard.internal.KeyboardParams -import helium314.keyboard.keyboard.internal.keyboard_parser.JsonKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL -import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.latin.R import helium314.keyboard.latin.common.FileUtils @@ -65,7 +64,7 @@ fun loadCustomLayout(layoutContent: String, layoutName: String, languageTag: Str .setPositiveButton(android.R.string.ok) { _, _ -> // name must be encoded to avoid issues with validity of subtype extra string or file name name = "$CUSTOM_LAYOUT_PREFIX${languageTag}.${encodeBase36(name)}.${if (isJson) "json" else "txt"}" - val file = getLayoutFile(name, context) + val file = getCustomLayoutFile(name, context) if (file.exists()) file.delete() file.parentFile?.mkdir() @@ -81,21 +80,21 @@ private fun checkLayout(layoutContent: String, context: Context): Boolean? { params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT) addLocaleKeyTextsToParams(context, params, POPUP_KEYS_NORMAL) try { - val keys = JsonKeyboardParser(params, context).parseLayoutString(layoutContent) + val keys = RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } if (!checkKeys(keys)) return null return true } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } try { - val keys = SimpleKeyboardParser(params, context).parseLayoutString(layoutContent) + val keys = RawKeyboardParser.parseSimpleString(layoutContent).map { it.map { it.toKeyParams(params) } } if (!checkKeys(keys)) return null return false } catch (e: Exception) { Log.w(TAG, "error parsing custom simple layout", e) } if (layoutContent.startsWith("[")) { - // layout can't be loaded, assume it's json -> try json layout again because of error message readout + // layout can't be loaded, assume it's json -> load json layout again because the error message shown to the user is from the most recent error try { - JsonKeyboardParser(params, context).parseLayoutString(layoutContent) + RawKeyboardParser.parseJsonString(layoutContent).map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } } catch (e: Exception) { Log.w(TAG, "error parsing custom json layout", e) } } return null @@ -129,8 +128,10 @@ private fun checkKeys(keys: List>): Boolean { return true } -fun getLayoutFile(layoutName: String, context: Context) = - File(Settings.getLayoutsDir(context), layoutName) +fun getCustomLayoutFile(layoutName: String, context: Context) = + File(getCustomLayoutsDir(context), layoutName) + +fun getCustomLayoutsDir(context: Context) = File(DeviceProtectedUtils.getFilesDir(context), "layouts") // undo the name changes in loadCustomLayout when clicking ok fun getLayoutDisplayName(layoutName: String) = @@ -141,11 +142,11 @@ fun getLayoutDisplayName(layoutName: String) = } fun removeCustomLayoutFile(layoutName: String, context: Context) { - getLayoutFile(layoutName, context).delete() + getCustomLayoutFile(layoutName, context).delete() } fun editCustomLayout(layoutName: String, context: Context, startContent: String? = null, displayName: CharSequence? = null) { - val file = getLayoutFile(layoutName, context) + val file = getCustomLayoutFile(layoutName, context) val editText = EditText(context).apply { setText(startContent ?: file.readText()) } diff --git a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt index 7ed43ac0d..a57f1a4e3 100644 --- a/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt +++ b/app/src/main/java/helium314/keyboard/latin/utils/SubtypeSettings.kt @@ -245,7 +245,7 @@ private fun loadResourceSubtypes(resources: Resources) { private fun removeInvalidCustomSubtypes(context: Context) { val prefs = DeviceProtectedUtils.getSharedPreferences(context) val additionalSubtypes = Settings.readPrefAdditionalSubtypes(prefs, context.resources).split(";") - val customSubtypeFiles by lazy { Settings.getLayoutsDir(context).list() } + val customSubtypeFiles by lazy { getCustomLayoutsDir(context).list() } val subtypesToRemove = mutableListOf() additionalSubtypes.forEach { val name = it.substringAfter(":").substringBefore(":") diff --git a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt index dec6dd8cd..4715ee577 100644 --- a/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt +++ b/app/src/test/java/helium314/keyboard/KeyboardParserTest.kt @@ -12,13 +12,10 @@ import helium314.keyboard.keyboard.internal.KeyboardBuilder import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.TouchPositionCorrection import helium314.keyboard.keyboard.internal.UniqueKeysCache -import helium314.keyboard.keyboard.internal.keyboard_parser.JsonKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.POPUP_KEYS_NORMAL -import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser +import helium314.keyboard.keyboard.internal.keyboard_parser.RawKeyboardParser import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyType -import helium314.keyboard.keyboard.internal.keyboard_parser.floris.MultiTextKeyData import helium314.keyboard.latin.LatinIME import helium314.keyboard.latin.RichInputMethodSubtype import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype @@ -120,7 +117,7 @@ f""", // no newline at the end val wantedKeyLabels = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) layoutStrings.forEachIndexed { i, layout -> println(i) - val keyLabels = SimpleKeyboardParser(params, latinIME).parseCoreLayout(layout).map { it.map { it.label } } + val keyLabels = RawKeyboardParser.parseSimpleString(layout).map { it.map { it.toKeyParams(params).mLabel } } assertEquals(wantedKeyLabels, keyLabels) } } @@ -268,7 +265,7 @@ f""", // no newline at the end ] ] """.trimIndent() - val keys = JsonKeyboardParser(params, latinIME).parseCoreLayout(layoutString) + val keys = RawKeyboardParser.parseJsonString(layoutString).map { it.mapNotNull { it.compute(params) } } keys.first().forEachIndexed { index, keyData -> println("data: key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}") val keyParams = keyData.toKeyParams(params) @@ -298,13 +295,13 @@ f""", // no newline at the end val editorInfo = EditorInfo() val subtype = createEmojiCapableAdditionalSubtype(Locale.GERMANY, "qwertz+", true) val (kb, keys) = buildKeyboard(editorInfo, subtype, KeyboardId.ELEMENT_ALPHABET) - assertEquals(keys[0].size, 11) - assertEquals(keys[1].size, 11) - assertEquals(keys[2].size, 10) + assertEquals(11, keys[0].size) + assertEquals(11, keys[1].size) + assertEquals(10, keys[2].size) val (kb2, keys2) = buildKeyboard(editorInfo, subtype, KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) - assertEquals(keys2[0].size, 11) - assertEquals(keys2[1].size, 11) - assertEquals(keys2[2].size, 10) + assertEquals(11, keys2[0].size) + assertEquals(11, keys2[1].size) + assertEquals(10, keys2[2].size) } @Test fun `popup key count does not depend on shift for (for simple layout)`() {