This commit is contained in:
Nehemiah of Zebulun 2023-08-29 10:59:03 +02:00
parent 1f589c985a
commit 9a9a37bc2b
6 changed files with 115 additions and 74 deletions

View File

@ -18,6 +18,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.mezimmah.wkt9.dao.SettingDao import net.mezimmah.wkt9.dao.SettingDao
import net.mezimmah.wkt9.dao.WordDao import net.mezimmah.wkt9.dao.WordDao
@ -51,6 +52,8 @@ class WKT9: InputMethodService() {
private var queryJob: Job? = null private var queryJob: Job? = null
private val ioScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private val ioScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var ioJob: Job? = null private var ioJob: Job? = null
private val commitScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
private var commitJob: Job? = null
private var cursorPosition = 0 private var cursorPosition = 0
private var longPressTimeout = 700 private var longPressTimeout = 700
@ -71,7 +74,8 @@ class WKT9: InputMethodService() {
private var composing = false private var composing = false
private val candidates: MutableList<String> = mutableListOf() private val candidates: MutableList<String> = mutableListOf()
private var candidateIndex = 0 private var candidateIndex = 0
private var sentenceStart = false private var inputStatus: Status = Status.CAP
private var timeout: Int? = null
// UI // UI
private lateinit var inputView: View private lateinit var inputView: View
@ -112,7 +116,7 @@ class WKT9: InputMethodService() {
inputMode = null inputMode = null
cursorPosition = 0 cursorPosition = 0
sentenceStart = false inputStatus = Status.CAP
} }
override fun onFinishInputView(finishingInput: Boolean) { override fun onFinishInputView(finishingInput: Boolean) {
@ -127,10 +131,10 @@ class WKT9: InputMethodService() {
return inputMode?.let { return inputMode?.let {
val keyEventResult = val keyEventResult =
if (repeatCount > 0) it.onKeyDownRepeatedly(key, repeatCount, sentenceStart) if (repeatCount > 0) it.onKeyDownRepeatedly(key, repeatCount, composing)
else { else {
event?.startTracking() event?.startTracking()
it.onKeyDown(key, sentenceStart) it.onKeyDown(key, composing)
} }
handleKeyEventResult(keyEventResult) handleKeyEventResult(keyEventResult)
@ -145,8 +149,8 @@ class WKT9: InputMethodService() {
return inputMode?.let { return inputMode?.let {
val keyEventResult = val keyEventResult =
if (keyDownMS >= longPressTimeout) it.afterKeyLongDown(key, keyDownMS, sentenceStart) if (keyDownMS >= longPressTimeout) it.afterKeyLongDown(key, keyDownMS, composing)
else it.afterKeyDown(key, sentenceStart) else it.afterKeyDown(key, composing)
handleKeyEventResult(keyEventResult) handleKeyEventResult(keyEventResult)
} ?: super.onKeyUp(keyCode, event) } ?: super.onKeyUp(keyCode, event)
@ -156,7 +160,7 @@ class WKT9: InputMethodService() {
val key = keypad.getKey(keyCode) ?: return super.onKeyLongPress(keyCode, event) val key = keypad.getKey(keyCode) ?: return super.onKeyLongPress(keyCode, event)
return inputMode?.let { return inputMode?.let {
val keyEventResult = it.onKeyLongDown(key, sentenceStart) val keyEventResult = it.onKeyLongDown(key, composing)
handleKeyEventResult(keyEventResult) handleKeyEventResult(keyEventResult)
} ?: super.onKeyLongPress(keyCode, event) } ?: super.onKeyLongPress(keyCode, event)
@ -166,9 +170,6 @@ class WKT9: InputMethodService() {
val inputType = attribute?.inputType?.and(InputType.TYPE_MASK_CLASS) ?: 0 val inputType = attribute?.inputType?.and(InputType.TYPE_MASK_CLASS) ?: 0
cursorPosition = attribute?.initialSelEnd ?: 0 cursorPosition = attribute?.initialSelEnd ?: 0
sentenceStart =
if (cursorPosition == 0) true
else isSentenceStart()
when (inputType) { when (inputType) {
InputType.TYPE_CLASS_DATETIME, InputType.TYPE_CLASS_DATETIME,
@ -180,7 +181,7 @@ class WKT9: InputMethodService() {
else -> Log.d(tag, "Mode without input...") else -> Log.d(tag, "Mode without input...")
} }
showStatusIcon(0) updateInputStatus()
super.onStartInput(attribute, restarting) super.onStartInput(attribute, restarting)
} }
@ -205,17 +206,6 @@ class WKT9: InputMethodService() {
) )
} }
override fun showStatusIcon(iconResId: Int) {
if (iconResId != 0) super.showStatusIcon(iconResId)
when (inputMode) {
is WordInputMode -> super.showStatusIcon(R.drawable.word)
is AlphaInputMode -> super.showStatusIcon(R.drawable.alpha)
is NumericInputMode -> super.showStatusIcon(R.drawable.numeric)
else -> super.showStatusIcon(R.drawable.wkt9)
}
}
private fun clearCandidates() { private fun clearCandidates() {
clearCandidateUI() clearCandidateUI()
@ -242,7 +232,7 @@ class WKT9: InputMethodService() {
private fun deleteText(beforeCursor: Int, afterCursor: Int) { private fun deleteText(beforeCursor: Int, afterCursor: Int) {
currentInputConnection?.deleteSurroundingText(beforeCursor, afterCursor) currentInputConnection?.deleteSurroundingText(beforeCursor, afterCursor)
sentenceStart = isSentenceStart() updateInputStatus()
} }
// Todo: inputType // Todo: inputType
@ -262,7 +252,9 @@ class WKT9: InputMethodService() {
private fun finishComposingText(): Boolean { private fun finishComposingText(): Boolean {
return if (composing) { return if (composing) {
composing = false composing = false
sentenceStart = isSentenceStart()
updateInputStatus()
currentInputConnection?.finishComposingText() ?: false currentInputConnection?.finishComposingText() ?: false
} else false } else false
} }
@ -276,11 +268,25 @@ class WKT9: InputMethodService() {
} }
} }
private fun handleComposeTimeout(timeout: Int?) {
this.timeout = timeout
commitJob?.cancel()
if (timeout == null) return
commitJob = commitScope.launch {
delay(timeout.toLong())
finishComposingText()
clearCandidates()
}
}
private fun handleKeyEventResult(res: KeyEventResult): Boolean { private fun handleKeyEventResult(res: KeyEventResult): Boolean {
if (res.finishComposing) finishComposingText() if (res.finishComposing) finishComposingText()
if (res.startComposing) markComposingRegion() if (res.startComposing) markComposingRegion()
if (!res.codeWord.isNullOrEmpty()) onCodeWordUpdate(res.codeWord) if (!res.codeWord.isNullOrEmpty()) onCodeWordUpdate(res.codeWord, res.timeout)
if (!res.candidates.isNullOrEmpty()) onCandidates(res.candidates) if (!res.candidates.isNullOrEmpty()) onCandidates(res.candidates, res.timeout)
if (res.deleteBeforeCursor > 0 || res.deleteAfterCursor > 0) onDelete(res.deleteBeforeCursor, res.deleteAfterCursor) if (res.deleteBeforeCursor > 0 || res.deleteAfterCursor > 0) onDelete(res.deleteBeforeCursor, res.deleteAfterCursor)
if (res.goHome) goHome() if (res.goHome) goHome()
if (res.left) onLeft() if (res.left) onLeft()
@ -288,6 +294,7 @@ class WKT9: InputMethodService() {
if (res.record) onRecord() if (res.record) onRecord()
if (res.transcribe) onTranscribe() if (res.transcribe) onTranscribe()
if (res.updateStatus) onUpdateStatus() if (res.updateStatus) onUpdateStatus()
// if (res.sentenceStart != null) sentenceStart = res.sentenceStart
if (res.focus) onFocus() if (res.focus) onFocus()
return res.consumed return res.consumed
@ -330,7 +337,7 @@ class WKT9: InputMethodService() {
return composing return composing
} }
private fun onCandidates(candidates: List<String>) { private fun onCandidates(candidates: List<String>, timeout: Int?) {
clearCandidates() clearCandidates()
candidates.forEach { candidates.forEach {
@ -339,9 +346,10 @@ class WKT9: InputMethodService() {
loadCandidates(candidateIndex) loadCandidates(candidateIndex)
composeText(candidates[candidateIndex]) composeText(candidates[candidateIndex])
handleComposeTimeout(timeout)
} }
private fun onCodeWordUpdate(codeWord: StringBuilder) { private fun onCodeWordUpdate(codeWord: StringBuilder, timeout: Int?) {
clearCandidates() clearCandidates()
queryJob?.cancel() queryJob?.cancel()
@ -352,6 +360,7 @@ class WKT9: InputMethodService() {
loadCandidates(candidateIndex) loadCandidates(candidateIndex)
composeText(candidates[candidateIndex], 1) composeText(candidates[candidateIndex], 1)
handleComposeTimeout(timeout)
} }
} }
@ -374,6 +383,7 @@ class WKT9: InputMethodService() {
clearCandidateUI() clearCandidateUI()
loadCandidates(candidateIndex) loadCandidates(candidateIndex)
composeText(candidates[candidateIndex]) composeText(candidates[candidateIndex])
handleComposeTimeout(this.timeout)
} }
private fun onRecord() { private fun onRecord() {
@ -422,6 +432,7 @@ class WKT9: InputMethodService() {
clearCandidateUI() clearCandidateUI()
loadCandidates(candidateIndex) loadCandidates(candidateIndex)
composeText(candidates[candidateIndex]) composeText(candidates[candidateIndex])
handleComposeTimeout(this.timeout)
} }
private fun onTranscribe() { private fun onTranscribe() {
@ -457,14 +468,44 @@ class WKT9: InputMethodService() {
Log.d(tag, "We're going to update the status...") Log.d(tag, "We're going to update the status...")
} }
private fun updateInputStatus() {
when (inputMode?.status) {
Status.UPPER -> {
inputStatus = Status.UPPER
showStatusIcon(R.drawable.shift)
}
Status.CAP -> {
if (isSentenceStart()) {
inputStatus = Status.CAP
showStatusIcon(R.drawable.shift)
} else {
inputStatus = Status.LOWER
showStatusIcon(R.drawable.word)
}
}
else -> {
inputStatus = Status.LOWER
showStatusIcon(R.drawable.word)
}
}
}
private suspend fun queryT9Candidates(codeWord: StringBuilder, limit: Int = 10): Boolean { private suspend fun queryT9Candidates(codeWord: StringBuilder, limit: Int = 10): Boolean {
val words = wordDao.findCandidates(codeWord.toString(), limit) val words = wordDao.findCandidates(codeWord.toString(), limit)
words.forEach { word -> words.forEach { word ->
val candidate = val candidate =
if (sentenceStart && inputMode?.status == Status.WORD_CAP) word.word.replaceFirstChar { it.uppercase() } when (inputStatus) {
else if (inputMode?.status == Status.WORD_UPPER) word.word.uppercase() Status.CAP -> word.word.replaceFirstChar { it.uppercase() }
else word.word Status.UPPER -> word.word.uppercase()
else -> word.word
}
candidates.add(candidate) candidates.add(candidate)
} }

View File

@ -4,7 +4,7 @@ import net.mezimmah.wkt9.keypad.Key
import net.mezimmah.wkt9.keypad.KeyEventResult import net.mezimmah.wkt9.keypad.KeyEventResult
class AlphaInputMode: InputMode { class AlphaInputMode: InputMode {
override var status: Status = Status.ALPHA_CAP override var status: Status = Status.CAP
private set private set
override fun onKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult { override fun onKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult {

View File

@ -6,13 +6,13 @@ import net.mezimmah.wkt9.keypad.KeyEventResult
interface InputMode { interface InputMode {
val status: Status val status: Status
fun onKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult fun onKeyDown(key: Key, composing: Boolean): KeyEventResult
fun onKeyLongDown(key: Key, sentenceStart: Boolean): KeyEventResult fun onKeyLongDown(key: Key, composing: Boolean): KeyEventResult
fun onKeyDownRepeatedly(key: Key, repeat: Int, sentenceStart: Boolean): KeyEventResult fun onKeyDownRepeatedly(key: Key, repeat: Int, composing: Boolean): KeyEventResult
fun afterKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult fun afterKeyDown(key: Key, composing: Boolean): KeyEventResult
fun afterKeyLongDown(key: Key, keyDownMS: Long, sentenceStart: Boolean): KeyEventResult fun afterKeyLongDown(key: Key, keyDownMS: Long, composing: Boolean): KeyEventResult
} }

View File

@ -1,11 +1,8 @@
package net.mezimmah.wkt9.inputmode package net.mezimmah.wkt9.inputmode
enum class Status(val idx: Int) { enum class Status(val idx: Int) {
WORD(0), CAP(0),
WORD_CAP(1), UPPER(1),
WORD_UPPER(2), LOWER(2),
ALPHA(3), NUM(3)
ALPHA_CAP(4),
ALPHA_UPPER(5),
NUM(6)
} }

View File

@ -15,21 +15,21 @@ class WordInputMode: InputMode {
private var keyIndex = 0 private var keyIndex = 0
private var lastKey: Key? = null private var lastKey: Key? = null
override var status: Status = Status.WORD_CAP override var status: Status = Status.CAP
private set private set
init { init {
Log.d(tag, "Started word input mode.") Log.d(tag, "Started word input mode.")
} }
override fun onKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult { override fun onKeyDown(key: Key, composing: Boolean): KeyEventResult {
keyStats(key) keyStats(key)
return when(keyCommandResolver.getCommand(key)) { return when(keyCommandResolver.getCommand(key)) {
Command.BACK -> KeyEventResult(false) Command.BACK -> KeyEventResult(false)
Command.CHARACTER -> buildCodeWord(key) Command.CHARACTER -> buildCodeWord(key)
Command.DELETE -> deleteCharacter() Command.DELETE -> deleteCharacter()
Command.SPACE -> finalizeWordOrSentence() Command.SPACE -> finalizeWordOrSentence(composing)
Command.LEFT -> navigateLeft() Command.LEFT -> navigateLeft()
Command.RIGHT -> navigateRight() Command.RIGHT -> navigateRight()
Command.SELECT -> focus() Command.SELECT -> focus()
@ -37,7 +37,7 @@ class WordInputMode: InputMode {
} }
} }
override fun onKeyLongDown(key: Key, sentenceStart: Boolean): KeyEventResult { override fun onKeyLongDown(key: Key, composing: Boolean): KeyEventResult {
return when(keyCommandResolver.getCommand(key, true)) { return when(keyCommandResolver.getCommand(key, true)) {
Command.RECORD -> record() Command.RECORD -> record()
Command.SWITCH_MODE -> switchMode() Command.SWITCH_MODE -> switchMode()
@ -45,7 +45,7 @@ class WordInputMode: InputMode {
} }
} }
override fun onKeyDownRepeatedly(key: Key, repeat: Int, sentenceStart: Boolean): KeyEventResult { override fun onKeyDownRepeatedly(key: Key, repeat: Int, composing: Boolean): KeyEventResult {
return when(keyCommandResolver.getCommand(key, repeat = repeat)) { return when(keyCommandResolver.getCommand(key, repeat = repeat)) {
Command.HOME -> goHome(repeat) Command.HOME -> goHome(repeat)
Command.DELETE -> deleteCharacter(repeat) Command.DELETE -> deleteCharacter(repeat)
@ -53,15 +53,15 @@ class WordInputMode: InputMode {
} }
} }
override fun afterKeyDown(key: Key, sentenceStart: Boolean): KeyEventResult { override fun afterKeyDown(key: Key, composing: Boolean): KeyEventResult {
return when(keyCommandResolver.getCommand(key, after = true)) { return when(keyCommandResolver.getCommand(key, after = true)) {
Command.BACK -> goBack() Command.BACK -> goBack()
Command.SHIFT_MODE -> shiftMode(sentenceStart) Command.SHIFT_MODE -> shiftMode(composing)
else -> KeyEventResult() else -> KeyEventResult()
} }
} }
override fun afterKeyLongDown(key: Key, keyDownMS: Long, sentenceStart: Boolean): KeyEventResult { override fun afterKeyLongDown(key: Key, keyDownMS: Long, composing: Boolean): KeyEventResult {
return when(keyCommandResolver.getCommand(key, after = true, longPress = true)) { return when(keyCommandResolver.getCommand(key, after = true, longPress = true)) {
Command.TRANSCRIBE -> transcribe() Command.TRANSCRIBE -> transcribe()
else -> KeyEventResult() else -> KeyEventResult()
@ -89,8 +89,8 @@ class WordInputMode: InputMode {
) )
} }
private fun finalizeWordOrSentence(): KeyEventResult { private fun finalizeWordOrSentence(composing: Boolean): KeyEventResult {
if (!newKey) return navigateRight() if (composing && !newKey) return navigateRight()
val finishComposing = codeWord.isNotEmpty() val finishComposing = codeWord.isNotEmpty()
@ -99,7 +99,8 @@ class WordInputMode: InputMode {
return KeyEventResult( return KeyEventResult(
finishComposing = finishComposing, finishComposing = finishComposing,
startComposing = true, startComposing = true,
candidates = listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ") candidates = listOf(" ", ". ", "? ", "! ", ", ", ": ", "; "),
timeout = 700
) )
} }
@ -171,33 +172,34 @@ class WordInputMode: InputMode {
lastKey = null lastKey = null
} }
private fun shiftMode(sentenceStart: Boolean): KeyEventResult { private fun shiftMode(composing: Boolean): KeyEventResult {
reset() Log.d(tag, "Composing: $composing")
if (status == Status.WORD_CAP && sentenceStart || status == Status.WORD && !sentenceStart) { return KeyEventResult()
return KeyEventResult( // reset()
consumed = true,
sentenceStart = !sentenceStart
)
}
status = when(status) { // if (status == Status.CAP && sentenceStart || status == Status.LOWER && !sentenceStart) {
Status.WORD_CAP -> Status.WORD_UPPER // return KeyEventResult(
Status.WORD -> Status.WORD_CAP // consumed = true,
else -> Status.WORD // sentenceStart = !sentenceStart
} // )
// }
//
// status = when(status) {
// Status.CAP -> Status.UPPER
// Status.LOWER -> Status.CAP
// else -> Status.LOWER
// }
return KeyEventResult( // return KeyEventResult(
consumed = true, // consumed = true,
updateStatus = true // updateStatus = true
) // )
} }
private fun switchMode(): KeyEventResult { private fun switchMode(): KeyEventResult {
reset() reset()
Log.d(tag, "Switch mode")
return KeyEventResult(true) return KeyEventResult(true)
} }

View File

@ -8,6 +8,7 @@ data class KeyEventResult(
val startComposing: Boolean = false, val startComposing: Boolean = false,
val codeWord: StringBuilder? = null, val codeWord: StringBuilder? = null,
val candidates: List<String>? = null, val candidates: List<String>? = null,
val timeout: Int? = null,
val deleteBeforeCursor: Int = 0, val deleteBeforeCursor: Int = 0,
val deleteAfterCursor: Int = 0, val deleteAfterCursor: Int = 0,
val goHome: Boolean = false, val goHome: Boolean = false,