mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-20 14:19:08 +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;
|
return popupKeysColumnAndFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only for testing
|
||||||
|
public String getOutputText() {
|
||||||
|
return mOptionalAttributes == null ? null : mOptionalAttributes.mOutputText;
|
||||||
|
}
|
||||||
|
|
||||||
public KeyParams(
|
public KeyParams(
|
||||||
@NonNull final String keySpec,
|
@NonNull final String keySpec,
|
||||||
@NonNull final KeyboardParams params,
|
@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.CaseSelector
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.CharWidthSelector
|
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.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.KeyData
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.LayoutDirectionSelector
|
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.MultiTextKeyData
|
||||||
|
@ -28,14 +27,14 @@ import helium314.keyboard.keyboard.internal.keyboard_parser.floris.VariationSele
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
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>> {
|
override fun parseCoreLayout(layoutContent: String): MutableList<List<KeyData>> {
|
||||||
val florisKeyData: List<List<AbstractKeyData>> = florisJsonConfig.decodeFromString(layoutContent)
|
val florisKeyData: List<List<AbstractKeyData>> = florisJsonConfig.decodeFromString(layoutContent)
|
||||||
// initially 200 ms parse (debug build on S4 mini)
|
// initially 200 ms parse (debug build on S4 mini)
|
||||||
// after a few parses it's optimized and 20-30 ms
|
// 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
|
// 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>()
|
val paramsRow = ArrayList<KeyParams>()
|
||||||
row.forEach { key ->
|
row.forEach { key ->
|
||||||
var keyParams: KeyParams? = null
|
var keyParams: KeyParams? = null
|
||||||
// try parsing a functional key, converting names from florisBoard to what is used here (should be unified)
|
// try parsing a functional key
|
||||||
// note that this is ignoring code on those keys, if any
|
// todo: note that this is ignoring code on those keys, if any
|
||||||
val functionalKeyName = when (key.label) {
|
val functionalKeyName = when (key.label) {
|
||||||
"view_characters" -> "alpha"
|
|
||||||
"view_symbols" -> "symbol"
|
|
||||||
"enter" -> "action"
|
|
||||||
// todo (later): maybe add special popupKeys for phone and number layouts?
|
// 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) "period" else "."
|
||||||
"," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "comma" else ","
|
"," -> if (params.mId.mElementId == KeyboardId.ELEMENT_NUMPAD) "comma" else ","
|
||||||
else -> key.label
|
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 {
|
try {
|
||||||
keyParams = getFunctionalKeyParams(functionalKeyName)
|
keyParams = getFunctionalKeyParams(functionalKeyName)
|
||||||
} catch (_: Throwable) {} // just use normal label
|
} 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
|
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
|
// 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))
|
if (locale.country.matches(euroCountries))
|
||||||
return euro
|
return euro
|
||||||
if (locale.toString().matches(euroLocales))
|
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 fun genericCurrencyKey(currency: String) = currency to genericCurrencyPopupKeys
|
||||||
private val genericCurrencyPopupKeys = arrayOf("£", "€", "$", "¢", "¥", "₱")
|
private val genericCurrencyPopupKeys = listOf("£", "€", "$", "¢", "¥", "₱")
|
||||||
|
|
||||||
private fun getCurrency(locale: Locale): String {
|
private fun getCurrency(locale: Locale): String {
|
||||||
if (locale.country == "BD") return "৳"
|
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
|
// needs at least 4 popupKeys for working shift-symbol keyboard
|
||||||
private val euro = "€" to arrayOf("£", "¥", "$", "¢", "₱")
|
private val euro = "€" to listOf("£", "¥", "$", "¢", "₱")
|
||||||
private val dram = "֏" to arrayOf("€", "₽", "$", "£", "¥")
|
private val dram = "֏" to listOf("€", "₽", "$", "£", "¥")
|
||||||
private val rupee = "₹" to arrayOf("£", "€", "$", "¢", "¥", "₱")
|
private val rupee = "₹" to listOf("£", "€", "$", "¢", "¥", "₱")
|
||||||
private val pound = "£" to arrayOf("€", "¥", "$", "¢", "₱")
|
private val pound = "£" to listOf("€", "¥", "$", "¢", "₱")
|
||||||
private val ruble = "₽" to arrayOf("€", "$", "£", "¥", "₱")
|
private val ruble = "₽" to listOf("€", "$", "£", "¥", "₱")
|
||||||
private val lira = "₺" to arrayOf("€", "$", "£", "¥", "₱")
|
private val lira = "₺" to listOf("€", "$", "£", "¥", "₱")
|
||||||
private val dollar = "$" to arrayOf("£", "¢", "€", "¥", "₱")
|
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 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()
|
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
|
const val INTERNAL_FLORIS_MAX = -1
|
||||||
val INTERNAL_FLORIS = INTERNAL_FLORIS_MIN..INTERNAL_FLORIS_MAX
|
val INTERNAL_FLORIS = INTERNAL_FLORIS_MIN..INTERNAL_FLORIS_MAX
|
||||||
val INTERNAL_HELI = -19999..-10000 // for keys exclusive to this app
|
val INTERNAL_HELI = -19999..-10000 // for keys exclusive to this app
|
||||||
|
val CURRENCY = CURRENCY_SLOT_6..CURRENCY_SLOT_1
|
||||||
}
|
}
|
||||||
|
|
||||||
const val UNSPECIFIED = 0
|
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
|
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 */
|
/** to make sure a FlorisBoard code works when reading a JSON layout */
|
||||||
private fun Int.checkOrConvertCode(): Int = if (this > 0) this else when (this) {
|
fun Int.checkAndConvertCode(): 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,
|
|
||||||
|
|
||||||
// working
|
// 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,
|
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,
|
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,
|
CLIPBOARD_SELECT_WORD, TOGGLE_INCOGNITO_MODE, TOGGLE_AUTOCORRECT, MOVE_START_OF_LINE, MOVE_END_OF_LINE,
|
||||||
|
@ -148,17 +147,19 @@ object KeyCode {
|
||||||
|
|
||||||
// conversion
|
// conversion
|
||||||
IME_UI_MODE_TEXT -> ALPHA
|
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")
|
else -> throw IllegalStateException("key code $this not yet supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** to make sure a FlorisBoard label works when reading a JSON layout */
|
/** 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_characters" -> "alpha"
|
||||||
"view_symbols" -> "symbol"
|
"view_symbols" -> "symbol"
|
||||||
"view_numeric_advanced" -> "numpad"
|
"view_numeric_advanced" -> "numpad"
|
||||||
"view_phone" -> "alpha"
|
"view_phone" -> "alpha" // phone keyboard is treated like alphabet, just with different layout
|
||||||
"view_phone2" -> "symbols"
|
"view_phone2" -> "symbols" // phone symbols
|
||||||
"ime_ui_mode_media" -> "emoji"
|
"ime_ui_mode_media" -> "emoji"
|
||||||
"ime_ui_mode_clipboard" -> "clipboard"
|
"ime_ui_mode_clipboard" -> "clipboard"
|
||||||
"ime_ui_mode_text" -> "alpha"
|
"ime_ui_mode_text" -> "alpha"
|
||||||
|
@ -168,12 +169,7 @@ object KeyCode {
|
||||||
"currency_slot_4" -> "$$$3"
|
"currency_slot_4" -> "$$$3"
|
||||||
"currency_slot_5" -> "$$$4"
|
"currency_slot_5" -> "$$$4"
|
||||||
"currency_slot_6" -> "$$$5"
|
"currency_slot_6" -> "$$$5"
|
||||||
|
"enter" -> "action"
|
||||||
else -> this
|
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 kotlinx.serialization.Transient
|
||||||
import helium314.keyboard.keyboard.Key
|
import helium314.keyboard.keyboard.Key
|
||||||
import helium314.keyboard.keyboard.internal.KeyboardParams
|
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.keyboard.internal.keyboard_parser.rtlLabel
|
||||||
import helium314.keyboard.latin.common.Constants
|
import helium314.keyboard.latin.common.Constants
|
||||||
import helium314.keyboard.latin.common.StringUtils
|
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
|
// 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 {
|
fun isSpaceKey(): Boolean {
|
||||||
return type == KeyType.CHARACTER && (code == Constants.CODE_SPACE || code == KeyCode.CJK_SPACE
|
return type == KeyType.CHARACTER && (code == Constants.CODE_SPACE || code == KeyCode.CJK_SPACE
|
||||||
|
@ -89,6 +124,18 @@ sealed interface KeyData : AbstractKeyData {
|
||||||
return if (code == KeyCode.UNSPECIFIED || code == KeyCode.MULTIPLE_CODE_POINTS) {
|
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)
|
// 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
|
// but also longer labels should work without issues, also for MultiTextKeyData
|
||||||
|
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(
|
Key.KeyParams(
|
||||||
label.rtlLabel(params), // todo (when supported): convert special labels to keySpec
|
label.rtlLabel(params), // todo (when supported): convert special labels to keySpec
|
||||||
params,
|
params,
|
||||||
|
@ -97,6 +144,7 @@ sealed interface KeyData : AbstractKeyData {
|
||||||
Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type
|
Key.BACKGROUND_TYPE_NORMAL, // todo (when supported): determine type
|
||||||
popup,
|
popup,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Key.KeyParams(
|
Key.KeyParams(
|
||||||
label.ifEmpty { StringUtils.newSingleCodePointString(code) },
|
label.ifEmpty { StringUtils.newSingleCodePointString(code) },
|
||||||
|
@ -132,23 +180,6 @@ class TextKeyData(
|
||||||
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
||||||
override val labelFlags: Int = 0
|
override val labelFlags: Int = 0
|
||||||
) : KeyData {
|
) : 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 {
|
override fun asString(isForDisplay: Boolean): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
if (isForDisplay || code == KeyCode.URI_COMPONENT_TLD || code < Constants.CODE_SPACE) {
|
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
|
@Serializable
|
||||||
@SerialName("auto_text_key")
|
@SerialName("auto_text_key")
|
||||||
class AutoTextKeyData(
|
class AutoTextKeyData(
|
||||||
|
@ -178,16 +211,6 @@ class AutoTextKeyData(
|
||||||
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
override val popup: PopupSet<AbstractKeyData> = PopupSet(),
|
||||||
override val labelFlags: Int = 0
|
override val labelFlags: Int = 0
|
||||||
) : KeyData {
|
) : 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 {
|
override fun asString(isForDisplay: Boolean): String {
|
||||||
return buildString {
|
return buildString {
|
||||||
|
@ -220,6 +243,8 @@ class MultiTextKeyData(
|
||||||
@Transient override val code: Int = KeyCode.MULTIPLE_CODE_POINTS
|
@Transient override val code: Int = KeyCode.MULTIPLE_CODE_POINTS
|
||||||
|
|
||||||
override fun compute(params: KeyboardParams): KeyData {
|
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
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,17 @@ import helium314.keyboard.keyboard.internal.KeyboardBuilder
|
||||||
import helium314.keyboard.keyboard.internal.KeyboardParams
|
import helium314.keyboard.keyboard.internal.KeyboardParams
|
||||||
import helium314.keyboard.keyboard.internal.TouchPositionCorrection
|
import helium314.keyboard.keyboard.internal.TouchPositionCorrection
|
||||||
import helium314.keyboard.keyboard.internal.UniqueKeysCache
|
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.POPUP_KEYS_NORMAL
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser
|
import helium314.keyboard.keyboard.internal.keyboard_parser.SimpleKeyboardParser
|
||||||
import helium314.keyboard.keyboard.internal.keyboard_parser.addLocaleKeyTextsToParams
|
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.LatinIME
|
||||||
import helium314.keyboard.latin.RichInputMethodSubtype
|
import helium314.keyboard.latin.RichInputMethodSubtype
|
||||||
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype
|
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype
|
||||||
|
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
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() {
|
@Test fun canLoadKeyboard() {
|
||||||
val editorInfo = EditorInfo()
|
val editorInfo = EditorInfo()
|
||||||
val subtype = createEmojiCapableAdditionalSubtype(Locale.ENGLISH, "qwerty", true)
|
val subtype = createEmojiCapableAdditionalSubtype(Locale.ENGLISH, "qwerty", true)
|
||||||
|
|
Loading…
Add table
Reference in a new issue