mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-19 13:49:13 +00:00
improve support for special keys and floris layouts (not fully there yet...)
This commit is contained in:
parent
c29eb0d03e
commit
b47bc52e7d
7 changed files with 241 additions and 67 deletions
|
@ -1027,6 +1027,11 @@ public class Key implements Comparable<Key> {
|
|||
return popupKeysColumnAndFlags;
|
||||
}
|
||||
|
||||
// only for testing
|
||||
public String getOutputText() {
|
||||
return mOptionalAttributes == null ? null : mOptionalAttributes.mOutputText;
|
||||
}
|
||||
|
||||
public KeyParams(
|
||||
@NonNull final String keySpec,
|
||||
@NonNull final KeyboardParams params,
|
||||
|
|
|
@ -11,7 +11,6 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.AutoTextKeyDa
|
|||
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.KeyCode.convertFloris
|
||||
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
|
||||
|
@ -28,14 +27,14 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSele
|
|||
* 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, private val context: Context) : KeyboardParser(params, context) {
|
||||
class JsonKeyboardParser(private val params: KeyboardParams, context: Context) : KeyboardParser(params, context) {
|
||||
|
||||
override fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>> {
|
||||
val florisKeyData: List<List<AbstractKeyData>> = 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)?.convertFloris() } }
|
||||
return florisKeyData.mapTo(mutableListOf()) { it.mapNotNull { it.compute(params) } }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -244,18 +244,15 @@ abstract class KeyboardParser(private val params: KeyboardParams, private val co
|
|||
val paramsRow = ArrayList<KeyParams>()
|
||||
row.forEach { key ->
|
||||
var keyParams: KeyParams? = null
|
||||
// try parsing a functional key, converting names from florisBoard to what is used here (should be unified)
|
||||
// note that this is ignoring code on those keys, if any
|
||||
// try parsing a functional key
|
||||
// todo: note that this is ignoring code on those keys, if any
|
||||
val functionalKeyName = when (key.label) {
|
||||
"view_characters" -> "alpha"
|
||||
"view_symbols" -> "symbol"
|
||||
"enter" -> "action"
|
||||
// todo (later): maybe add special popupKeys for phone and number layouts?
|
||||
"." -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "period" else "."
|
||||
"," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "comma" else ","
|
||||
else -> key.label
|
||||
}
|
||||
if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) {
|
||||
if (functionalKeyName.length > 1 && key.type != KeyType.NUMERIC) { // todo: why exception for numeric?
|
||||
try {
|
||||
keyParams = getFunctionalKeyParams(functionalKeyName)
|
||||
} catch (_: Throwable) {} // just use normal label
|
||||
|
|
|
@ -271,7 +271,7 @@ private const val READER_MODE_LABELS = 3
|
|||
private const val READER_MODE_NUMBER_ROW = 4
|
||||
|
||||
// probably could be improved and extended, currently this is what's done in key_styles_currency.xml
|
||||
private fun getCurrencyKey(locale: Locale): Pair<String, Array<String>> {
|
||||
private fun getCurrencyKey(locale: Locale): Pair<String, List<String>> {
|
||||
if (locale.country.matches(euroCountries))
|
||||
return euro
|
||||
if (locale.toString().matches(euroLocales))
|
||||
|
@ -298,7 +298,7 @@ private fun getCurrencyKey(locale: Locale): Pair<String, Array<String>> {
|
|||
}
|
||||
|
||||
private fun genericCurrencyKey(currency: String) = currency to genericCurrencyPopupKeys
|
||||
private val genericCurrencyPopupKeys = arrayOf("£", "€", "$", "¢", "¥", "₱")
|
||||
private val genericCurrencyPopupKeys = listOf("£", "€", "$", "¢", "¥", "₱")
|
||||
|
||||
private fun getCurrency(locale: Locale): String {
|
||||
if (locale.country == "BD") return "৳"
|
||||
|
@ -320,13 +320,13 @@ private fun getCurrency(locale: Locale): String {
|
|||
}
|
||||
|
||||
// needs at least 4 popupKeys for working shift-symbol keyboard
|
||||
private val euro = "€" to arrayOf("£", "¥", "$", "¢", "₱")
|
||||
private val dram = "֏" to arrayOf("€", "₽", "$", "£", "¥")
|
||||
private val rupee = "₹" to arrayOf("£", "€", "$", "¢", "¥", "₱")
|
||||
private val pound = "£" to arrayOf("€", "¥", "$", "¢", "₱")
|
||||
private val ruble = "₽" to arrayOf("€", "$", "£", "¥", "₱")
|
||||
private val lira = "₺" to arrayOf("€", "$", "£", "¥", "₱")
|
||||
private val dollar = "$" to arrayOf("£", "¢", "€", "¥", "₱")
|
||||
private val euro = "€" to listOf("£", "¥", "$", "¢", "₱")
|
||||
private val dram = "֏" to listOf("€", "₽", "$", "£", "¥")
|
||||
private val rupee = "₹" to listOf("£", "€", "$", "¢", "¥", "₱")
|
||||
private val pound = "£" to listOf("€", "¥", "$", "¢", "₱")
|
||||
private val ruble = "₽" to listOf("€", "$", "£", "¥", "₱")
|
||||
private val lira = "₺" to listOf("€", "$", "£", "¥", "₱")
|
||||
private val dollar = "$" to listOf("£", "¢", "€", "¥", "₱")
|
||||
private val euroCountries = "AD|AT|BE|BG|HR|CY|CZ|DA|EE|FI|FR|DE|GR|HU|IE|IT|XK|LV|LT|LU|MT|MO|ME|NL|PL|PT|RO|SM|SK|SI|ES|VA".toRegex()
|
||||
private val euroLocales = "bg|ca|cs|da|de|el|en|es|et|eu|fi|fr|ga|gl|hr|hu|it|lb|lt|lv|mt|nl|pl|pt|ro|sk|sl|sq|sr|sv".toRegex()
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ object KeyCode {
|
|||
const val INTERNAL_FLORIS_MAX = -1
|
||||
val INTERNAL_FLORIS = INTERNAL_FLORIS_MIN..INTERNAL_FLORIS_MAX
|
||||
val INTERNAL_HELI = -19999..-10000 // for keys exclusive to this app
|
||||
val CURRENCY = CURRENCY_SLOT_6..CURRENCY_SLOT_1
|
||||
}
|
||||
|
||||
const val UNSPECIFIED = 0
|
||||
|
@ -131,11 +132,9 @@ object KeyCode {
|
|||
const val NOT_SPECIFIED = -10008 // todo: not sure if there is need to have the "old" unspecified keyCode different, just test it and maybe merge
|
||||
|
||||
/** to make sure a FlorisBoard code works when reading a JSON layout */
|
||||
private fun Int.checkOrConvertCode(): Int = if (this > 0) this else when (this) {
|
||||
// todo: should work, but not yet
|
||||
// CURRENCY_SLOT_1, CURRENCY_SLOT_2, CURRENCY_SLOT_3, CURRENCY_SLOT_4, CURRENCY_SLOT_5, CURRENCY_SLOT_6,
|
||||
|
||||
fun Int.checkAndConvertCode(): Int = if (this > 0) this else when (this) {
|
||||
// working
|
||||
CURRENCY_SLOT_1, CURRENCY_SLOT_2, CURRENCY_SLOT_3, CURRENCY_SLOT_4, CURRENCY_SLOT_5, CURRENCY_SLOT_6,
|
||||
VOICE_INPUT, LANGUAGE_SWITCH, SETTINGS, DELETE, ALPHA, SYMBOL, EMOJI, CLIPBOARD,
|
||||
UNDO, REDO, ARROW_DOWN, ARROW_UP, ARROW_RIGHT, ARROW_LEFT, CLIPBOARD_COPY, CLIPBOARD_SELECT_ALL,
|
||||
CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE,
|
||||
|
@ -148,17 +147,19 @@ object KeyCode {
|
|||
|
||||
// conversion
|
||||
IME_UI_MODE_TEXT -> ALPHA
|
||||
VIEW_PHONE -> ALPHA // phone keyboard is treated like alphabet, just with different layout
|
||||
VIEW_PHONE2 -> SYMBOL
|
||||
|
||||
else -> throw IllegalStateException("key code $this not yet supported")
|
||||
}
|
||||
|
||||
/** to make sure a FlorisBoard label works when reading a JSON layout */
|
||||
private fun String.convertFlorisLabel(): String = when (this) {
|
||||
fun String.convertFlorisLabel(): String = when (this) {
|
||||
"view_characters" -> "alpha"
|
||||
"view_symbols" -> "symbol"
|
||||
"view_numeric_advanced" -> "numpad"
|
||||
"view_phone" -> "alpha"
|
||||
"view_phone2" -> "symbols"
|
||||
"view_phone" -> "alpha" // phone keyboard is treated like alphabet, just with different layout
|
||||
"view_phone2" -> "symbols" // phone symbols
|
||||
"ime_ui_mode_media" -> "emoji"
|
||||
"ime_ui_mode_clipboard" -> "clipboard"
|
||||
"ime_ui_mode_text" -> "alpha"
|
||||
|
@ -168,12 +169,7 @@ object KeyCode {
|
|||
"currency_slot_4" -> "$$$3"
|
||||
"currency_slot_5" -> "$$$4"
|
||||
"currency_slot_6" -> "$$$5"
|
||||
"enter" -> "action"
|
||||
else -> this
|
||||
}
|
||||
|
||||
fun KeyData.convertFloris() = when (this) {
|
||||
is TextKeyData -> { TextKeyData(type, code.checkOrConvertCode(), label.convertFlorisLabel(), groupId, popup, labelFlags) }
|
||||
is AutoTextKeyData -> { AutoTextKeyData(type, code.checkOrConvertCode(), label.convertFlorisLabel(), groupId, popup, labelFlags) }
|
||||
is MultiTextKeyData -> { MultiTextKeyData(type, codePoints, label.convertFlorisLabel(), groupId, popup, labelFlags) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import kotlinx.serialization.Serializable
|
|||
import kotlinx.serialization.Transient
|
||||
import helium314.keyboard.keyboard.Key
|
||||
import helium314.keyboard.keyboard.internal.KeyboardParams
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.checkAndConvertCode
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode.convertFlorisLabel
|
||||
import helium314.keyboard.keyboard.internal.keyboard_parser.rtlLabel
|
||||
import helium314.keyboard.latin.common.Constants
|
||||
import helium314.keyboard.latin.common.StringUtils
|
||||
|
@ -71,7 +73,40 @@ sealed interface KeyData : AbstractKeyData {
|
|||
}
|
||||
|
||||
// make it non-nullable for simplicity, and to reflect current implementations
|
||||
override fun compute(params: KeyboardParams): KeyData
|
||||
override fun compute(params: KeyboardParams): KeyData {
|
||||
val newLabel = label.convertFlorisLabel()
|
||||
val newCode = code.checkAndConvertCode()
|
||||
|
||||
// resolve currency keys
|
||||
if (newLabel.startsWith("$$$") || newCode in KeyCode.Spec.CURRENCY) {
|
||||
val currencyKey = params.mLocaleKeyboardInfos.currencyKey
|
||||
val currencyCodeAsString = if (newCode in KeyCode.Spec.CURRENCY) {
|
||||
when (newCode) {
|
||||
KeyCode.CURRENCY_SLOT_1 -> "|" + currencyKey.first
|
||||
KeyCode.CURRENCY_SLOT_2 -> "|" + currencyKey.second[0]
|
||||
KeyCode.CURRENCY_SLOT_3 -> "|" + currencyKey.second[1]
|
||||
KeyCode.CURRENCY_SLOT_4 -> "|" + currencyKey.second[2]
|
||||
KeyCode.CURRENCY_SLOT_5 -> "|" + currencyKey.second[3]
|
||||
KeyCode.CURRENCY_SLOT_6 -> "|" + currencyKey.second[4]
|
||||
else -> ""
|
||||
}
|
||||
} else ""
|
||||
if (newLabel == "$$$") {
|
||||
val finalLabel = currencyKey.first + currencyCodeAsString
|
||||
// the flag is to match old parser, but why is it there for main currency key and not for others?
|
||||
return TextKeyData(type, KeyCode.UNSPECIFIED, finalLabel, groupId, SimplePopups(currencyKey.second), labelFlags or Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO)
|
||||
}
|
||||
val n = newLabel.substringAfter("$$$").toIntOrNull()
|
||||
if (n != null && n <= 5 && n > 0) {
|
||||
val finalLabel = currencyKey.second[n - 1] + currencyCodeAsString
|
||||
return TextKeyData(type, KeyCode.UNSPECIFIED, finalLabel, groupId, popup, labelFlags)
|
||||
}
|
||||
}
|
||||
if (newCode != code || newLabel != label)
|
||||
return TextKeyData(type, newCode, newLabel, groupId, popup, labelFlags).compute(params)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
fun isSpaceKey(): Boolean {
|
||||
return type == KeyType.CHARACTER && (code == Constants.CODE_SPACE || code == KeyCode.CJK_SPACE
|
||||
|
@ -89,14 +124,27 @@ sealed interface KeyData : AbstractKeyData {
|
|||
return if (code == KeyCode.UNSPECIFIED || code == KeyCode.MULTIPLE_CODE_POINTS) {
|
||||
// code will be determined from label if possible (i.e. label is single code point)
|
||||
// but also longer labels should work without issues, also for MultiTextKeyData
|
||||
Key.KeyParams(
|
||||
label.rtlLabel(params), // todo (when supported): convert special labels to keySpec
|
||||
params,
|
||||
width,
|
||||
labelFlags or additionalLabelFlags,
|
||||
Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type
|
||||
popup,
|
||||
)
|
||||
if (this is MultiTextKeyData) {
|
||||
val outputText = String(codePoints, 0, codePoints.size)
|
||||
Key.KeyParams(
|
||||
"$label|$outputText",
|
||||
code,
|
||||
params,
|
||||
width,
|
||||
labelFlags or additionalLabelFlags,
|
||||
Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type
|
||||
popup,
|
||||
)
|
||||
} else {
|
||||
Key.KeyParams(
|
||||
label.rtlLabel(params), // todo (when supported): convert special labels to keySpec
|
||||
params,
|
||||
width,
|
||||
labelFlags or additionalLabelFlags,
|
||||
Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type
|
||||
popup,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Key.KeyParams(
|
||||
label.ifEmpty { StringUtils.newSingleCodePointString(code) },
|
||||
|
@ -132,23 +180,6 @@ class TextKeyData(
|
|||
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
||||
override val labelFlags: Int = 0
|
||||
) : KeyData {
|
||||
override fun compute(params: KeyboardParams): KeyData {
|
||||
// if (evaluator.isSlot(this)) { // todo: currency key stuff probably should be taken from florisboard too
|
||||
// return evaluator.slotData(this)?.let { data ->
|
||||
// TextKeyData(type, data.code, data.label, groupId, popup).compute(params)
|
||||
// }
|
||||
// }
|
||||
if (label.startsWith("$$$")) { // currency key
|
||||
if (label == "$$$")
|
||||
return params.mLocaleKeyboardInfos.currencyKey
|
||||
.let { it.first.toTextKey(it.second.toList(), labelFlags = Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO) } // the flag is to match old parser, but why for main currency key, but not for others?
|
||||
val n = label.substringAfter("$$$").toIntOrNull()
|
||||
if (n != null && n <= 5 && n > 0)
|
||||
return params.mLocaleKeyboardInfos.currencyKey.second[n - 1].toTextKey()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun asString(isForDisplay: Boolean): String {
|
||||
return buildString {
|
||||
if (isForDisplay || code == KeyCode.URI_COMPONENT_TLD || code < Constants.CODE_SPACE) {
|
||||
|
@ -168,6 +199,8 @@ class TextKeyData(
|
|||
|
||||
}
|
||||
|
||||
// AutoTextKeyData is just for converting case with shift, which HeliBoard always does anyway
|
||||
// (maybe change later if there is a use case)
|
||||
@Serializable
|
||||
@SerialName("auto_text_key")
|
||||
class AutoTextKeyData(
|
||||
|
@ -178,16 +211,6 @@ class AutoTextKeyData(
|
|||
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
||||
override val labelFlags: Int = 0
|
||||
) : KeyData {
|
||||
// state and recompute not needed, as upcasing is done when creating KeyParams
|
||||
|
||||
override fun compute(params: KeyboardParams): KeyData {
|
||||
// if (evaluator.isSlot(this)) { // todo: see above
|
||||
// return evaluator.slotData(this)?.let { data ->
|
||||
// TextKeyData(type, data.code, data.label, groupId, popup).compute(evaluator)
|
||||
// }
|
||||
// }
|
||||
return this
|
||||
}
|
||||
|
||||
override fun asString(isForDisplay: Boolean): String {
|
||||
return buildString {
|
||||
|
@ -220,6 +243,8 @@ class MultiTextKeyData(
|
|||
@Transient override val code: Int = KeyCode.MULTIPLE_CODE_POINTS
|
||||
|
||||
override fun compute(params: KeyboardParams): KeyData {
|
||||
// todo: does this work? maybe convert label to | style?
|
||||
// but if i allow negative codes, ctrl+z could be on a single key (but floris doesn't support this anyway)
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,17 @@ 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.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
|
||||
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -114,6 +119,153 @@ f""", // no newline at the end
|
|||
}
|
||||
}
|
||||
|
||||
@Test fun jsonParser() {
|
||||
val params = KeyboardParams()
|
||||
params.mId = KeyboardLayoutSet.getFakeKeyboardId(KeyboardId.ELEMENT_ALPHABET)
|
||||
params.mPopupKeyTypes.add(POPUP_KEYS_LAYOUT)
|
||||
addLocaleKeyTextsToParams(latinIME, params, POPUP_KEYS_NORMAL)
|
||||
data class Expected(val label: String, val text: String?, val code: Int, val popups: List<String>?)
|
||||
val expected = listOf(
|
||||
Expected("a", null, 'a'.code, null),
|
||||
Expected("a", null, 'a'.code, null),
|
||||
Expected("$", null, '$'.code, listOf("£", "€", "¢", "¥", "₱")),
|
||||
Expected("$", null, '¥'.code, listOf("£", "€", "¢", "¥", "₱")),
|
||||
Expected("i", null, 105, null),
|
||||
Expected("্র", "্র", KeyCode.MULTIPLE_CODE_POINTS, null),
|
||||
Expected("x", "্র", KeyCode.MULTIPLE_CODE_POINTS, null),
|
||||
Expected(";", null, ';'.code, listOf(":")),
|
||||
Expected(".", null, '.'.code, listOf(">")),
|
||||
Expected("'", null, '\''.code, listOf("!", "\"")),
|
||||
Expected("9", null, '9'.code, null), // todo (later): also should have different background or whatever is related to type
|
||||
Expected("", null, -7, null), // todo: expect an icon
|
||||
Expected("?123", null, -207, null),
|
||||
Expected("", null, ' '.code, null),
|
||||
Expected("(", null, '('.code, listOf("<", "[", "{")),
|
||||
Expected("$", null, '$'.code, listOf("£", "₱", "€", "¢", "¥")),
|
||||
Expected("p", null, 'p'.code, null),
|
||||
)
|
||||
val layoutString = """
|
||||
[
|
||||
[
|
||||
{ "$": "auto_text_key" "label": "a" },
|
||||
{ "$": "text_key" "label": "a" },
|
||||
{ "label": "$$$" },
|
||||
{ "label": "$$$", code: -805 },
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 105, "label": "i" },
|
||||
"upper": { "code": 304, "label": "İ" }
|
||||
},
|
||||
{ "$": "multi_text_key", "codePoints": [2509, 2480], "label": "্র" },
|
||||
{ "$": "multi_text_key", "codePoints": [2509, 2480], "label": "x" },
|
||||
{ "$": "case_selector",
|
||||
"lower": { "code": 59, "label": ";", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 58, "label": ":" }
|
||||
]
|
||||
} },
|
||||
"upper": { "code": 58, "label": ":", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 59, "label": ";" }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": { "code": 62, "label": ">", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 46, "label": "." }
|
||||
]
|
||||
} },
|
||||
"default": { "code": 46, "label": ".", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 62, "label": ">" }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "$": "shift_state_selector",
|
||||
"shiftedManual": { "code": 34, "label": "\"", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 39, "label": "'"}
|
||||
]
|
||||
} },
|
||||
"default": { "$": "variation_selector",
|
||||
"email": { "code": 64, "label": "@" },
|
||||
"uri": { "code": 47, "label": "/" },
|
||||
"default": { "code": 39, "label": "'", "popup": {
|
||||
"relevant": [
|
||||
{ "code": 33, "label": "!" },
|
||||
{ "code": 34, "label": "\"" }
|
||||
]
|
||||
} }
|
||||
}
|
||||
},
|
||||
{ "code": 57, "label": "9", "type": "numeric" },
|
||||
{ "code": -7, "label": "delete", "type": "enter_editing" },
|
||||
{ "code": -207, "label": "view_phone2", "type": "system_gui" },
|
||||
{ "code": 32, "label": "space" },
|
||||
{ "$": "layout_direction_selector",
|
||||
"ltr": { "code": 40, "label": "(", "popup": {
|
||||
"main": { "code": 60, "label": "<" },
|
||||
"relevant": [
|
||||
{ "code": 91, "label": "[" },
|
||||
{ "code": 123, "label": "{" }
|
||||
]
|
||||
} },
|
||||
"rtl": { "code": 41, "label": "(", "popup": {
|
||||
"main": { "code": 62, "label": "<" },
|
||||
"relevant": [
|
||||
{ "code": 93, "label": "[" },
|
||||
{ "code": 125, "label": "{" }
|
||||
]
|
||||
} }
|
||||
},
|
||||
{ "code": -801, "label": "currency_slot_1", "popup": {
|
||||
"main": { "code": -802, "label": "currency_slot_2" },
|
||||
"relevant": [
|
||||
{ "code": -806, "label": "currency_slot_6" },
|
||||
{ "code": -803, "label": "currency_slot_3" },
|
||||
{ "code": -804, "label": "currency_slot_4" },
|
||||
{ "code": -805, "label": "currency_slot_5" }
|
||||
]
|
||||
} },
|
||||
{ "label": "p" }
|
||||
],
|
||||
[
|
||||
{ "label": "q" },
|
||||
{ "label": "s" },
|
||||
{ "label": "d" },
|
||||
{ "label": "f" },
|
||||
{ "label": "g" },
|
||||
{ "label": "h" },
|
||||
{ "label": "j" },
|
||||
{ "label": "k" },
|
||||
{ "label": "l" },
|
||||
{ "label": "m", "popup": { "main": { "label": "/" } } }
|
||||
],
|
||||
[
|
||||
{ "label": "w" },
|
||||
{ "label": "x" },
|
||||
{ "label": "c" },
|
||||
{ "label": "v" },
|
||||
{ "label": "b" },
|
||||
{ "label": "n" }
|
||||
]
|
||||
]
|
||||
""".trimIndent()
|
||||
val keys = JsonKeyboardParser(params, latinIME).parseCoreLayout(layoutString)
|
||||
keys.first().forEachIndexed { index, keyData ->
|
||||
println("key ${keyData.label}: code ${keyData.code}, popups: ${keyData.popup.getPopupKeyLabels(params)}")
|
||||
if (keyData.type == KeyType.ENTER_EDITING || keyData.type == KeyType.SYSTEM_GUI) return@forEachIndexed // todo: currently not accepted, but should be (see below)
|
||||
val keyParams = keyData.toKeyParams(params)
|
||||
println("key ${keyParams.mLabel}: code ${keyParams.mCode}, popups: ${keyParams.mPopupKeys?.toList()}")
|
||||
if (keyParams.outputText == "space") return@forEachIndexed // todo: only works for numeric layouts... idea: parse space anywhere, and otherwise only if special type
|
||||
assertEquals(expected[index].label, keyParams.mLabel)
|
||||
assertEquals(expected[index].code, keyParams.mCode)
|
||||
assertEquals(expected[index].popups?.sorted(), keyParams.mPopupKeys?.mapNotNull { it.mLabel }?.sorted()) // todo (later): what's wrong with order?
|
||||
assertEquals(expected[index].text, keyParams.outputText)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun canLoadKeyboard() {
|
||||
val editorInfo = EditorInfo()
|
||||
val subtype = createEmojiCapableAdditionalSubtype(Locale.ENGLISH, "qwerty", true)
|
||||
|
|
Loading…
Add table
Reference in a new issue