Initial
This commit is contained in:
16
app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt
Normal file
16
app/src/main/java/net/mezimmah/wkt9/keypad/Command.kt
Normal 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
|
||||
}
|
||||
22
app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt
Normal file
22
app/src/main/java/net/mezimmah/wkt9/keypad/Key.kt
Normal 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')
|
||||
}
|
||||
50
app/src/main/java/net/mezimmah/wkt9/keypad/KeyCodeMapping.kt
Normal file
50
app/src/main/java/net/mezimmah/wkt9/keypad/KeyCodeMapping.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
app/src/main/java/net/mezimmah/wkt9/keypad/KeyEventResult.kt
Normal file
12
app/src/main/java/net/mezimmah/wkt9/keypad/KeyEventResult.kt
Normal 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
|
||||
)
|
||||
33
app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt
Normal file
33
app/src/main/java/net/mezimmah/wkt9/keypad/KeyLayout.kt
Normal 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('*','#','\'','"','.','?','!',',','-','@','$','/','%',':','(',')')
|
||||
}
|
||||
75
app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt
Normal file
75
app/src/main/java/net/mezimmah/wkt9/keypad/Keypad.kt
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user