mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-24 08:36:26 +00:00
stricter checks for json layouts (and fix an old bug in hebrew layout)
This commit is contained in:
parent
2700c3428a
commit
ecf7aabdb1
4 changed files with 56 additions and 8 deletions
|
@ -2,12 +2,12 @@
|
||||||
[
|
[
|
||||||
{ "$": "variation_selector",
|
{ "$": "variation_selector",
|
||||||
"email": { "label": "-" },
|
"email": { "label": "-" },
|
||||||
"url": { "label": "-" },
|
"uri": { "label": "-" },
|
||||||
"default": { "label": "'", "popup": { "relevant": [{ "label": "׳" }, { "label": "״" }, { "label": "\"" }] } }
|
"default": { "label": "'", "popup": { "relevant": [{ "label": "׳" }, { "label": "״" }, { "label": "\"" }] } }
|
||||||
},
|
},
|
||||||
{ "$": "variation_selector",
|
{ "$": "variation_selector",
|
||||||
"email": { "label": "_" },
|
"email": { "label": "_" },
|
||||||
"url": { "label": "_" },
|
"uri": { "label": "_" },
|
||||||
"default": { "label": "-", "popup": { "relevant": [{ "label": "־" }, { "label": "_" }] } }
|
"default": { "label": "-", "popup": { "relevant": [{ "label": "־" }, { "label": "_" }] } }
|
||||||
},
|
},
|
||||||
{ "label": "ק", "popup": {
|
{ "label": "ק", "popup": {
|
||||||
|
|
|
@ -541,7 +541,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Displays a toast-like message with the provided text for a specified duration.
|
// Displays a toast-like message with the provided text for a specified duration.
|
||||||
public void showFakeToast(final String text, final int timeMillis) {
|
private void showFakeToast(final String text, final int timeMillis) {
|
||||||
if (mFakeToastView.getVisibility() == View.VISIBLE) return;
|
if (mFakeToastView.getVisibility() == View.VISIBLE) return;
|
||||||
|
|
||||||
final Drawable appIcon = mFakeToastView.getCompoundDrawables()[0];
|
final Drawable appIcon = mFakeToastView.getCompoundDrawables()[0];
|
||||||
|
|
|
@ -27,6 +27,7 @@ import helium314.keyboard.latin.utils.ScriptUtils.script
|
||||||
import helium314.keyboard.latin.utils.getCustomFunctionalLayoutName
|
import helium314.keyboard.latin.utils.getCustomFunctionalLayoutName
|
||||||
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
import helium314.keyboard.latin.utils.getCustomLayoutFile
|
||||||
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
import helium314.keyboard.latin.utils.getCustomLayoutFiles
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import kotlinx.serialization.modules.polymorphic
|
import kotlinx.serialization.modules.polymorphic
|
||||||
|
@ -69,7 +70,9 @@ object RawKeyboardParser {
|
||||||
* codes of multi_text_key not used, only the label
|
* 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
|
* (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<List<AbstractKeyData>> = florisJsonConfig.decodeFromString(layoutText.stripCommentLines())
|
fun parseJsonString(layoutText: String, strict: Boolean = true): List<List<AbstractKeyData>> =
|
||||||
|
if (strict) checkJsonConfig.decodeFromString(layoutText.stripCommentLines())
|
||||||
|
else florisJsonConfig.decodeFromString(layoutText.stripCommentLines())
|
||||||
|
|
||||||
/** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */
|
/** Parse simple layouts, defined only as rows of (normal) keys with popup keys. */
|
||||||
fun parseSimpleString(layoutText: String): List<List<KeyData>> {
|
fun parseSimpleString(layoutText: String): List<List<KeyData>> {
|
||||||
|
@ -90,7 +93,7 @@ object RawKeyboardParser {
|
||||||
val layoutFileName = getLayoutFileName(layoutName, context)
|
val layoutFileName = getLayoutFileName(layoutName, context)
|
||||||
val layoutText = if (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
val layoutText = if (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
||||||
try {
|
try {
|
||||||
getCustomLayoutFile(layoutFileName, context).readText()
|
getCustomLayoutFile(layoutFileName, context).readText().trimStart()
|
||||||
} catch (e: Exception) { // fall back to defaults if for some reason file is broken
|
} catch (e: Exception) { // fall back to defaults if for some reason file is broken
|
||||||
val name = when {
|
val name = when {
|
||||||
layoutName.contains("functional") -> "functional_keys.json"
|
layoutName.contains("functional") -> "functional_keys.json"
|
||||||
|
@ -102,15 +105,17 @@ object RawKeyboardParser {
|
||||||
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
|
context.assets.open("layouts${File.separator}$name").reader().use { it.readText() }
|
||||||
}
|
}
|
||||||
} else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() }
|
} else context.assets.open("layouts${File.separator}$layoutFileName").reader().use { it.readText() }
|
||||||
if (layoutFileName.endsWith(".json") || layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX)) {
|
if (layoutFileName.endsWith(".json") || (layoutFileName.startsWith(CUSTOM_LAYOUT_PREFIX) && layoutText.startsWith("["))) {
|
||||||
try {
|
try {
|
||||||
val florisKeyData = parseJsonString(layoutText)
|
val florisKeyData = parseJsonString(layoutText, false)
|
||||||
return { params ->
|
return { params ->
|
||||||
florisKeyData.mapTo(mutableListOf()) { row ->
|
florisKeyData.mapTo(mutableListOf()) { row ->
|
||||||
row.mapNotNullTo(mutableListOf()) { it.compute(params) }
|
row.mapNotNullTo(mutableListOf()) { it.compute(params) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_: Exception) { }
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "could not parse json layout for $layoutName, falling back to simple layout parsing", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// not a json, or invalid json
|
// not a json, or invalid json
|
||||||
val simpleKeyData = parseSimpleString(layoutText)
|
val simpleKeyData = parseSimpleString(layoutText)
|
||||||
|
@ -178,7 +183,9 @@ object RawKeyboardParser {
|
||||||
* modified
|
* modified
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
private val florisJsonConfig = Json {
|
private val florisJsonConfig = Json {
|
||||||
|
allowTrailingComma = true
|
||||||
classDiscriminator = "$"
|
classDiscriminator = "$"
|
||||||
encodeDefaults = true
|
encodeDefaults = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
|
@ -205,4 +212,36 @@ object RawKeyboardParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy of florisJsonConfig, but with ignoreUnknownKeys = false so users get warned
|
||||||
|
// this is not default because users may have old layouts that should not stop working on app upgrade
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
private val checkJsonConfig = Json {
|
||||||
|
allowTrailingComma = true
|
||||||
|
classDiscriminator = "$"
|
||||||
|
encodeDefaults = true
|
||||||
|
ignoreUnknownKeys = false
|
||||||
|
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(KeyboardStateSelector::class, KeyboardStateSelector.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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,6 +458,15 @@ f""", // no newline at the end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun parseExistingLayouts() {
|
||||||
|
latinIME.assets.list("layouts")?.forEach {
|
||||||
|
val content = latinIME.assets.open("layouts/$it").reader().readText()
|
||||||
|
if (it.endsWith(".json"))
|
||||||
|
RawKeyboardParser.parseJsonString(content)
|
||||||
|
else RawKeyboardParser.parseSimpleString(content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private data class Expected(val code: Int, val label: String? = null, val icon: String? = null, val text: String? = null, val popups: List<Pair<String?, Int>>? = null)
|
private data class Expected(val code: Int, val label: String? = null, val icon: String? = null, val text: String? = null, val popups: List<Pair<String?, Int>>? = null)
|
||||||
|
|
||||||
private fun assertIsExpected(json: String, expected: Expected) {
|
private fun assertIsExpected(json: String, expected: Expected) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue