Making progress
This commit is contained in:
Generated
+123
@@ -0,0 +1,123 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
Generated
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
Binary file not shown.
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"artifactType": {
|
||||||
|
"type": "APK",
|
||||||
|
"kind": "Directory"
|
||||||
|
},
|
||||||
|
"applicationId": "net.mezimmah.wkt9",
|
||||||
|
"variantName": "release",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "SINGLE",
|
||||||
|
"filters": [],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 1,
|
||||||
|
"versionName": "1.0",
|
||||||
|
"outputFile": "app-release.apk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"elementType": "File"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.text.InputType
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import net.mezimmah.wkt9.WKT9IME
|
||||||
|
import net.mezimmah.wkt9.IME
|
||||||
|
import net.mezimmah.wkt9.inputmode.InputMode
|
||||||
|
import net.mezimmah.wkt9.inputmode.TextPositionInfo
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyLayout
|
||||||
|
import net.mezimmah.wkt9.keypad.Keypad
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
open class DefaultInputHandler(
|
||||||
|
private val wkt9: IME,
|
||||||
|
private val context: WKT9IME
|
||||||
|
) {
|
||||||
|
protected val tag = "WKT9"
|
||||||
|
|
||||||
|
protected var keypad: Keypad = Keypad()
|
||||||
|
protected var capMode: Int = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||||
|
|
||||||
|
protected open fun capMode(key: Key): Int? {
|
||||||
|
val modes = listOf(
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_WORDS,
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
var index = modes.indexOf(capMode)
|
||||||
|
|
||||||
|
when (key) {
|
||||||
|
Key.B2 -> {
|
||||||
|
if (index == 0) index = modes.count()
|
||||||
|
|
||||||
|
index--
|
||||||
|
}
|
||||||
|
else -> index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return modes[index % modes.count()]
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun finalizeWordOrSentence(stats: KeyEventStat) {
|
||||||
|
val candidates = ArrayList<CharSequence>()
|
||||||
|
|
||||||
|
wkt9.onCandidates(
|
||||||
|
candidates = candidates,
|
||||||
|
current = stats.repeats % candidates.count()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun getTextPositionInfo(text: CharSequence): TextPositionInfo {
|
||||||
|
val trimmed = text.trimEnd()
|
||||||
|
val regex = "[.!?]$".toRegex()
|
||||||
|
val startSentence = text.isEmpty() || regex.containsMatchIn(trimmed)
|
||||||
|
val startWord = text.isEmpty() || (startSentence || trimmed.length < text.length)
|
||||||
|
|
||||||
|
return TextPositionInfo(
|
||||||
|
startSentence = startSentence,
|
||||||
|
startWord = startWord
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getDefaultCapMode(typeFlags: Int): Int? {
|
||||||
|
val modes = listOf(
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_WORDS,
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
|
||||||
|
)
|
||||||
|
|
||||||
|
modes.forEach {
|
||||||
|
if (typeFlags.and(it) == it) return it
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun triggerKeyEvent(keyCode: Int) {
|
||||||
|
val down = KeyEvent(KeyEvent.ACTION_DOWN, keyCode)
|
||||||
|
val up = KeyEvent(KeyEvent.ACTION_UP, keyCode)
|
||||||
|
|
||||||
|
wkt9.onTriggerKeyEvent(down)
|
||||||
|
wkt9.onTriggerKeyEvent(up)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun triggerOriginalKeyEvent(key: Key) {
|
||||||
|
triggerKeyEvent(key.keyCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import net.mezimmah.wkt9.WKT9IME
|
||||||
|
import net.mezimmah.wkt9.IME
|
||||||
|
import net.mezimmah.wkt9.entity.Word
|
||||||
|
import net.mezimmah.wkt9.inputmode.InputMode
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class IdleInputHandler(
|
||||||
|
val ime: IME,
|
||||||
|
private var wkt9: WKT9IME,
|
||||||
|
) : DefaultInputHandler(ime, wkt9), InputHandler {
|
||||||
|
override fun onFinishComposing() {}
|
||||||
|
|
||||||
|
override fun onLongClickCandidate(text: String) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) {
|
||||||
|
when (command) {
|
||||||
|
Command.DIAL -> dial()
|
||||||
|
Command.CAMERA -> triggerKeyEvent(KeyEvent.KEYCODE_CAMERA)
|
||||||
|
// Command.NUMBER -> triggerOriginalKeyEvent(key)
|
||||||
|
else -> Log.d(tag, "Command not implemented: $command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwitchLocale(locale: Locale) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWordSelected(word: Word) {}
|
||||||
|
|
||||||
|
private fun dial() {
|
||||||
|
val uri = "tel:"
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
intent.data = Uri.parse(uri)
|
||||||
|
|
||||||
|
// wkt9.onStartIntent(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import net.mezimmah.wkt9.entity.Word
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
interface InputHandler {
|
||||||
|
fun onFinishComposing()
|
||||||
|
|
||||||
|
fun onLongClickCandidate(text: String)
|
||||||
|
|
||||||
|
fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat)
|
||||||
|
|
||||||
|
fun onSwitchLocale(locale: Locale)
|
||||||
|
|
||||||
|
fun onWordSelected(word: Word)
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.InputType
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.textservice.SentenceSuggestionsInfo
|
||||||
|
import android.view.textservice.SpellCheckerSession
|
||||||
|
import android.view.textservice.SuggestionsInfo
|
||||||
|
import android.view.textservice.TextServicesManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.mezimmah.wkt9.R
|
||||||
|
import net.mezimmah.wkt9.WKT9IME
|
||||||
|
import net.mezimmah.wkt9.IME
|
||||||
|
import net.mezimmah.wkt9.db.AppDatabase
|
||||||
|
import net.mezimmah.wkt9.entity.Word
|
||||||
|
import net.mezimmah.wkt9.inputmode.InputMode
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyLayout
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class LetterInputHandler(
|
||||||
|
val ime: IME,
|
||||||
|
private var wkt9: WKT9IME,
|
||||||
|
private var locale: Locale,
|
||||||
|
): SpellCheckerSession.SpellCheckerSessionListener, DefaultInputHandler(ime, wkt9), InputHandler {
|
||||||
|
private val db = AppDatabase.getInstance(wkt9)
|
||||||
|
private val wordDao = db.getWordDao()
|
||||||
|
|
||||||
|
private var lastKey: Key? = null
|
||||||
|
private var repeats: Int = 0
|
||||||
|
|
||||||
|
private var spellCheckerSession: SpellCheckerSession? = null
|
||||||
|
|
||||||
|
private var sentenceStart: Boolean = false
|
||||||
|
private var wordStart: Boolean = false
|
||||||
|
|
||||||
|
private val timeoutScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
|
private var timeoutJob: Job? = null
|
||||||
|
|
||||||
|
private val content = StringBuilder()
|
||||||
|
|
||||||
|
init {
|
||||||
|
val textServiceManager = wkt9.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE) as TextServicesManager
|
||||||
|
|
||||||
|
spellCheckerSession = textServiceManager.newSpellCheckerSession(
|
||||||
|
null,
|
||||||
|
locale,
|
||||||
|
this,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finalizeWordOrSentence(stats: KeyEventStat) {
|
||||||
|
timeoutJob?.cancel()
|
||||||
|
|
||||||
|
val ends = listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ")
|
||||||
|
val index = stats.repeats % ends.count()
|
||||||
|
val lastIndex = if (stats.repeats > 0) (stats.repeats - 1) % ends.count() else null
|
||||||
|
var beforeCursor = 0
|
||||||
|
|
||||||
|
if (lastIndex != null) beforeCursor += ends[lastIndex].length
|
||||||
|
|
||||||
|
wkt9.onCommit(ends[index], beforeCursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwitchLocale(locale: Locale) {
|
||||||
|
this.locale = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetSuggestions(results: Array<out SuggestionsInfo>?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGetSentenceSuggestions(results: Array<out SentenceSuggestionsInfo>?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinishComposing() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClickCandidate(text: String) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) {
|
||||||
|
when (command) {
|
||||||
|
Command.CAP_MODE -> capMode(key)
|
||||||
|
Command.CHARACTER -> composeCharacter(key, stats)
|
||||||
|
Command.DELETE -> delete()
|
||||||
|
Command.INPUT_MODE -> inputMode(key)
|
||||||
|
Command.NUMBER -> triggerOriginalKeyEvent(key)
|
||||||
|
// Command.RECORD -> wkt9.onRecord(true)
|
||||||
|
Command.SPACE -> finalizeWordOrSentence(stats)
|
||||||
|
// Command.TRANSCRIBE -> wkt9.onTranscribe()
|
||||||
|
else -> Log.d(tag, "Command not implemented: $command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWordSelected(word: Word) {}
|
||||||
|
|
||||||
|
private fun composeCharacter(key: Key, stats: KeyEventStat) {
|
||||||
|
if (lastKey == key) repeats++
|
||||||
|
else {
|
||||||
|
wkt9.onCommit()
|
||||||
|
|
||||||
|
lastKey = key
|
||||||
|
repeats = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val layout = KeyLayout.chars[key] ?: return
|
||||||
|
val index = repeats % layout.count()
|
||||||
|
|
||||||
|
wkt9.onCompose(layout[index].toString())
|
||||||
|
setComposeTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setComposeTimeout() {
|
||||||
|
timeoutJob?.cancel()
|
||||||
|
|
||||||
|
timeoutJob = timeoutScope.launch {
|
||||||
|
delay(400L)
|
||||||
|
wkt9.onCommit("")
|
||||||
|
|
||||||
|
lastKey = null
|
||||||
|
repeats = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun delete() {
|
||||||
|
if (timeoutJob?.isActive == true) {
|
||||||
|
timeoutJob?.cancel()
|
||||||
|
wkt9.onCompose("")
|
||||||
|
} else {
|
||||||
|
wkt9.onDeleteText(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inputMode(key: Key) {
|
||||||
|
if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Word)
|
||||||
|
else wkt9.onSwitchInputHandler(InputMode.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateIcon() {
|
||||||
|
val icon = when (capMode) {
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS -> R.drawable.letter_upper
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_WORDS,
|
||||||
|
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES -> if (wordStart) R.drawable.letter_cap else R.drawable.letter_lower
|
||||||
|
else -> R.drawable.letter_lower
|
||||||
|
}
|
||||||
|
|
||||||
|
// wkt9.onUpdateStatusIcon(icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// override fun onGetSentenceSuggestions(results: Array<out SentenceSuggestionsInfo>?) {
|
||||||
|
// results?.map {
|
||||||
|
// val suggestions = it.getSuggestionsInfoAt(0)
|
||||||
|
//
|
||||||
|
// for (index in 0 until suggestions.suggestionsCount) {
|
||||||
|
// val suggestion = suggestions.getSuggestionAt(index)
|
||||||
|
//
|
||||||
|
// candidates.add(suggestion)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (candidates.isEmpty()) return
|
||||||
|
//
|
||||||
|
// candidateSource = CandidateSource.Dictionary
|
||||||
|
//
|
||||||
|
// wkt9.onCandidates(
|
||||||
|
// candidates = candidates,
|
||||||
|
// current = null
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private fun getSuggestions() {
|
||||||
|
// val lastWord = getLastWord()
|
||||||
|
//
|
||||||
|
// if (lastWord.length < 3) return
|
||||||
|
//
|
||||||
|
// val words = arrayOf(
|
||||||
|
// TextInfo(
|
||||||
|
// lastWord.plus("#"), // Add hash to string to get magic performance
|
||||||
|
// 0,
|
||||||
|
// lastWord.length + 1, // We added the hash, remember
|
||||||
|
// 0,
|
||||||
|
// 0
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// spellCheckerSession?.getSentenceSuggestions(words, 15)
|
||||||
|
// }
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import net.mezimmah.wkt9.WKT9IME
|
||||||
|
import net.mezimmah.wkt9.IME
|
||||||
|
import net.mezimmah.wkt9.entity.Word
|
||||||
|
import net.mezimmah.wkt9.inputmode.InputMode
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class NumberInputHandler(
|
||||||
|
val ime: IME,
|
||||||
|
private var wkt9: WKT9IME,
|
||||||
|
) : InputHandler {
|
||||||
|
private val tag = "WKT9"
|
||||||
|
|
||||||
|
// We don't need to implement methods below
|
||||||
|
override fun onFinishComposing() {}
|
||||||
|
override fun onLongClickCandidate(text: String) {}
|
||||||
|
override fun onSwitchLocale(locale: Locale) {}
|
||||||
|
override fun onWordSelected(word: Word) {}
|
||||||
|
|
||||||
|
override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) {
|
||||||
|
when (command) {
|
||||||
|
Command.INPUT_MODE -> inputMode(key)
|
||||||
|
else -> Log.d(tag, "Command not implemented: $command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inputMode(key: Key) {
|
||||||
|
if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Letter)
|
||||||
|
else wkt9.onSwitchInputHandler(InputMode.Word)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package net.mezimmah.wkt9.inputhandler
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import net.mezimmah.wkt9.WKT9IME
|
||||||
|
import net.mezimmah.wkt9.IME
|
||||||
|
import net.mezimmah.wkt9.db.AppDatabase
|
||||||
|
import net.mezimmah.wkt9.entity.Word
|
||||||
|
import net.mezimmah.wkt9.inputmode.InputMode
|
||||||
|
import net.mezimmah.wkt9.keypad.Command
|
||||||
|
import net.mezimmah.wkt9.keypad.Key
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyEventStat
|
||||||
|
import net.mezimmah.wkt9.keypad.KeyLayout
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class WordInputHandler(
|
||||||
|
val ime: IME,
|
||||||
|
private var wkt9: WKT9IME,
|
||||||
|
private var locale: Locale,
|
||||||
|
) : DefaultInputHandler(ime, wkt9), InputHandler {
|
||||||
|
private val codeword = StringBuilder()
|
||||||
|
private var staleCodeword = false
|
||||||
|
private var lastSelectedWord: Word? = null
|
||||||
|
|
||||||
|
private val db = AppDatabase.getInstance(wkt9)
|
||||||
|
private val wordDao = db.getWordDao()
|
||||||
|
|
||||||
|
private val queryScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
|
private val ioScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
private var queryJob: Job? = null
|
||||||
|
|
||||||
|
override fun capMode(key: Key): Int? {
|
||||||
|
return super.capMode(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finalizeWordOrSentence(stats: KeyEventStat) {
|
||||||
|
val ends = listOf(" ", ". ", "? ", "! ", ", ", ": ", "; ")
|
||||||
|
val index = stats.repeats % ends.count()
|
||||||
|
val lastIndex = if (stats.repeats > 0) (stats.repeats - 1) % ends.count() else null
|
||||||
|
var beforeCursor = 0
|
||||||
|
|
||||||
|
if (lastIndex != null) beforeCursor += ends[lastIndex].length
|
||||||
|
|
||||||
|
wkt9.onCommit(ends[index], beforeCursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwitchLocale(locale: Locale) {
|
||||||
|
this.locale = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFinishComposing() {
|
||||||
|
queryJob?.cancel()
|
||||||
|
codeword.clear()
|
||||||
|
increaseWordWeight()
|
||||||
|
|
||||||
|
staleCodeword = false
|
||||||
|
lastSelectedWord = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLongClickCandidate(text: String) {
|
||||||
|
ioScope.launch {
|
||||||
|
wordDao.delete(text, locale.language)
|
||||||
|
|
||||||
|
handleCodewordChange(codeword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRunCommand(command: Command, key: Key, event: KeyEvent, stats: KeyEventStat) {
|
||||||
|
when (command) {
|
||||||
|
// Command.CAP_MODE -> capMode(key)
|
||||||
|
Command.CHARACTER -> buildCodeword(key)
|
||||||
|
Command.DELETE -> delete()
|
||||||
|
Command.ENTER -> enter(key)
|
||||||
|
Command.INPUT_MODE -> inputMode(key)
|
||||||
|
Command.MOVE_CURSOR -> moveCursor(key)
|
||||||
|
Command.NUMBER -> triggerOriginalKeyEvent(key)
|
||||||
|
Command.SPACE -> finalizeWordOrSentence(stats)
|
||||||
|
else -> Log.d(tag, "Command not implemented: $command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onWordSelected(word: Word) {
|
||||||
|
lastSelectedWord = word
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildCodeword(key: Key) {
|
||||||
|
// Don't build fruitless codeword
|
||||||
|
if (staleCodeword) return
|
||||||
|
|
||||||
|
val code = KeyLayout.numeric[key]
|
||||||
|
|
||||||
|
codeword.append(code)
|
||||||
|
queryJob?.cancel()
|
||||||
|
|
||||||
|
handleCodewordChange(codeword)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun delete() {
|
||||||
|
lastSelectedWord = null
|
||||||
|
|
||||||
|
if (codeword.length > 1) {
|
||||||
|
codeword.deleteAt(codeword.length - 1)
|
||||||
|
handleCodewordChange(codeword)
|
||||||
|
} else if (codeword.isNotEmpty()) {
|
||||||
|
codeword.clear()
|
||||||
|
wkt9.onCompose("")
|
||||||
|
} else {
|
||||||
|
wkt9.onDeleteText(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enter(key: Key) {
|
||||||
|
if (codeword.isNotEmpty()) wkt9.onCommit("")
|
||||||
|
else triggerOriginalKeyEvent(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleCodewordChange(codeword: StringBuilder) {
|
||||||
|
queryJob?.cancel()
|
||||||
|
|
||||||
|
queryJob = queryScope.launch(Dispatchers.Main) {
|
||||||
|
val words = wordDao.findCandidates(locale.language, codeword.toString(), 10)
|
||||||
|
|
||||||
|
// The codeword is stale when it does not yield any candidates in the DB
|
||||||
|
staleCodeword = words.isEmpty()
|
||||||
|
|
||||||
|
if (words.isNotEmpty()) wkt9.onWords(words)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun increaseWordWeight() {
|
||||||
|
val lastWord = lastSelectedWord ?: return
|
||||||
|
|
||||||
|
queryScope.launch {
|
||||||
|
wordDao.increaseWeight(lastWord.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inputMode(key: Key) {
|
||||||
|
if (key == Key.B4) wkt9.onSwitchInputHandler(InputMode.Number)
|
||||||
|
else wkt9.onSwitchInputHandler(InputMode.Letter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moveCursor(key: Key) {
|
||||||
|
if (codeword.isEmpty()) triggerOriginalKeyEvent(key)
|
||||||
|
else if (key == Key.RIGHT) wkt9.onNextWord()
|
||||||
|
else if (key == Key.LEFT) wkt9.onPreviousWord()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DiscouragedApi")
|
||||||
|
private fun updateIcon() {
|
||||||
|
// val mode = when (capMode) {
|
||||||
|
// InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS -> "upper"
|
||||||
|
// InputType.TYPE_TEXT_FLAG_CAP_WORDS,
|
||||||
|
// InputType.TYPE_TEXT_FLAG_CAP_SENTENCES -> if (wordStart) "cap" else "lower"
|
||||||
|
// else -> "lower"
|
||||||
|
// }
|
||||||
|
// val name = "word_${locale}_${mode}".replace('-', '_').lowercase()
|
||||||
|
// val icon = context.resources.getIdentifier(name, "drawable", context.packageName)
|
||||||
|
|
||||||
|
// wkt9.onUpdateStatusIcon(icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user