This commit is contained in:
zb
2023-08-24 21:09:06 +02:00
commit 40a21c6588
67 changed files with 81748 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
package net.mezimmah.wkt9.keypad
enum class Command {
CHARACTER,
NUMBER,
SPACE,
NEWLINE,
DELETE,
CYCLE_CANDIDATES,
SELECT,
SHIFT_MODE,
SWITCH_MODE,
NAVIGATE,
RECORD,
TRANSCRIBE
}

View File

@@ -0,0 +1,22 @@
package net.mezimmah.wkt9.keypad
enum class Key(val code: Char) {
N0('0'),
N1('1'),
N2('2'),
N3('3'),
N4('4'),
N5('5'),
N6('6'),
N7('7'),
N8('8'),
N9('9'),
STAR('*'),
POUND('#'),
UP('a'),
DOWN('b'),
LEFT('c'),
RIGHT('d'),
SELECT('e'),
DELETE('f')
}

View File

@@ -0,0 +1,50 @@
package net.mezimmah.wkt9.keypad
import java.util.Properties
class KeyCodeMapping(
private val keyMap: Map<Int, Key>,
) {
fun key(keyCode: Int): Key? {
return keyMap[keyCode]
}
companion object {
val basic = mapOf(
7 to Key.N0,
8 to Key.N1,
9 to Key.N2,
10 to Key.N3,
11 to Key.N4,
12 to Key.N5,
13 to Key.N6,
14 to Key.N7,
15 to Key.N8,
16 to Key.N9,
17 to Key.STAR,
18 to Key.POUND,
82 to Key.DELETE,
19 to Key.UP,
20 to Key.DOWN,
21 to Key.LEFT,
22 to Key.RIGHT,
23 to Key.SELECT
)
fun fromProperties(props: Properties): KeyCodeMapping {
val keyMap = HashMap<Int, Key>()
this.basic.forEach {
val keyCode = props.getProperty("key.${it.value.name}")?.toInt() ?: it.key
keyMap[keyCode] = it.value
}
return KeyCodeMapping(keyMap)
}
fun default(): KeyCodeMapping {
return KeyCodeMapping(basic)
}
}
}

View File

@@ -0,0 +1,83 @@
package net.mezimmah.wkt9.keypad
class KeyCommandResolver (
private val onShort: HashMap<Key, Command> = HashMap(mapOf()),
private val onLong: HashMap<Key, Command> = HashMap(mapOf()),
private val afterShort: HashMap<Key, Command> = HashMap(mapOf()),
private val afterLong: HashMap<Key, Command> = HashMap(mapOf()),
private val onRepeat: HashMap<Key, Command> = HashMap(mapOf()),
private val parent: KeyCommandResolver? = null
) {
fun getCommand(key: Key, longPress: Boolean = false, after: Boolean = false, repeat: Int = 0): Command? {
val command = when {
repeat > 0 -> onRepeat[key]
(longPress && after) -> afterLong[key]
(longPress) -> onLong[key]
(after) -> afterShort[key]
else -> onShort[key]
}
return when (command) {
null -> parent?.getCommand(key, longPress, after)
else -> command
}
}
companion object {
fun getBasic(): KeyCommandResolver {
return KeyCommandResolver(
onShort = HashMap(mapOf(
Key.N0 to Command.SPACE,
Key.N1 to Command.CHARACTER,
Key.N2 to Command.CHARACTER,
Key.N3 to Command.CHARACTER,
Key.N4 to Command.CHARACTER,
Key.N5 to Command.CHARACTER,
Key.N6 to Command.CHARACTER,
Key.N7 to Command.CHARACTER,
Key.N8 to Command.CHARACTER,
Key.N9 to Command.CHARACTER,
Key.LEFT to Command.NAVIGATE,
Key.RIGHT to Command.NAVIGATE,
Key.UP to Command.NAVIGATE,
Key.DOWN to Command.NAVIGATE,
Key.STAR to Command.CYCLE_CANDIDATES,
Key.DELETE to Command.DELETE,
Key.SELECT to Command.SELECT
)),
onLong = HashMap(mapOf(
Key.N0 to Command.NUMBER,
Key.N1 to Command.NUMBER,
Key.N2 to Command.NUMBER,
Key.N3 to Command.NUMBER,
Key.N4 to Command.NUMBER,
Key.N5 to Command.NUMBER,
Key.N6 to Command.NUMBER,
Key.N7 to Command.NUMBER,
Key.N8 to Command.NUMBER,
Key.N9 to Command.NUMBER,
Key.POUND to Command.SWITCH_MODE,
Key.SELECT to Command.RECORD
)),
afterShort = HashMap(mapOf(
Key.POUND to Command.SHIFT_MODE,
)),
afterLong = HashMap(mapOf(
Key.DELETE to Command.DELETE,
Key.SELECT to Command.TRANSCRIBE,
)),
onRepeat = HashMap(mapOf(
Key.DELETE to Command.DELETE,
))
)
}
}
}

View File

@@ -0,0 +1,12 @@
package net.mezimmah.wkt9.keypad
import java.lang.StringBuilder
data class KeyEventResult(
val consumed: Boolean = true,
val finishComposing: Boolean = false,
val startComposing: Boolean = false,
val codeWord: StringBuilder? = null,
val candidates: List<String>? = null,
val right: Boolean = false
)

View File

@@ -0,0 +1,33 @@
package net.mezimmah.wkt9.keypad
object KeyLayout {
// Map for number input mode
val numeric = mapOf(
Key.N0 to '0',
Key.N1 to '1',
Key.N2 to '2',
Key.N3 to '3',
Key.N4 to '4',
Key.N5 to '5',
Key.N6 to '6',
Key.N7 to '7',
Key.N8 to '8',
Key.N9 to '9',
)
val en_US = mapOf(
Key.N1 to listOf('.','?','!',',','-','\'','"','@','$','/','%',':','(',')'),
Key.N2 to listOf('a','b','c','ä','æ','å','à','á','â','ã','ç'),
Key.N3 to listOf('d','e','f','è','é','ê','ë','đ'),
Key.N4 to listOf('g','h','i','ì','í','î','ï'),
Key.N5 to listOf('j','k','l','£'),
Key.N6 to listOf('m','n','o','ö','ø','ò','ó','ô','õ','õ'),
Key.N7 to listOf('p','q','r','s','ß','$'),
Key.N8 to listOf('t','u','v','ù','ú','û','ü'),
Key.N9 to listOf('w','x','y','z','ý','þ'),
Key.STAR to listOf('*'),
Key.POUND to listOf('#'),
)
val nonAlphaNumeric = setOf('*','#','\'','"','.','?','!',',','-','@','$','/','%',':','(',')')
}

View File

@@ -0,0 +1,75 @@
package net.mezimmah.wkt9.keypad
import net.mezimmah.wkt9.exception.MissingLetterCode
import java.lang.StringBuilder
class Keypad(
private val keyCodeMapping: KeyCodeMapping,
private val letterLayout: Map<Key, List<Char>>
) {
private lateinit var letterKeyMap: Map<Char, Key>
private lateinit var keyIsLetterMap: Map<Key, Boolean>
private var emojiCodes = mapOf(
"❤️" to "143278",
"\uD83D\uDE18" to "15477"
)
init {
initKeyMaps(letterLayout)
}
fun getKey(keyCode: Int): Key? {
return keyCodeMapping.key(keyCode)
}
fun getCharacter(key: Key, idx: Int): Char {
val chars = letterLayout[key]!!
val length = chars.size
val wrappedIdx = idx % (length - 1) + 1
return chars[wrappedIdx]
}
fun getDigit(key: Key): Char? {
val letters = letterLayout[key] ?: return null
return if (letters.isNotEmpty() && letters[0].isDigit()) letters[0] else null
}
fun getCodeForWord(word: String): String {
if (emojiCodes.keys.contains(word)) return emojiCodes[word]!!
val builder = StringBuilder()
for (letter in word) {
val code = codeForLetter(letter) ?: throw MissingLetterCode("No code found for '$letter'")
builder.append(code)
}
return builder.toString()
}
private fun codeForLetter(letter: Char): Char? {
return letterKeyMap[letter]?.code
}
private fun initKeyMaps(layout: Map<Key, List<Char>>): Map<Char, Key> {
val letterKeyMap = HashMap<Char, Key>()
val keyIsLetterMap = HashMap<Key, Boolean>()
this.letterKeyMap = letterKeyMap
this.keyIsLetterMap = keyIsLetterMap
for ((key, characters) in layout) {
for (char in characters) {
letterKeyMap[char] = key
if (!KeyLayout.nonAlphaNumeric.contains(char)) {
letterKeyMap[char.uppercaseChar()] = key
keyIsLetterMap[key] = true
} else if (keyIsLetterMap[key] == null) keyIsLetterMap[key] = false
}
}
return letterKeyMap
}
}