Making progress
This commit is contained in:
parent
a7e17eda9d
commit
ea361c1586
123
.idea/codeStyles/Project.xml
generated
Normal file
123
.idea/codeStyles/Project.xml
generated
Normal file
@ -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>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
BIN
app/release/app-release.apk
Normal file
BIN
app/release/app-release.apk
Normal file
Binary file not shown.
20
app/release/output-metadata.json
Normal file
20
app/release/output-metadata.json
Normal file
@ -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"
|
||||||
|
}
|
239650
app/src/main/assets/de/words.txt
Normal file
239650
app/src/main/assets/de/words.txt
Normal file
File diff suppressed because it is too large
Load Diff
29038
app/src/main/assets/es/words.txt
Normal file
29038
app/src/main/assets/es/words.txt
Normal file
File diff suppressed because it is too large
Load Diff
198161
app/src/main/assets/nl/words.txt
Normal file
198161
app/src/main/assets/nl/words.txt
Normal file
File diff suppressed because it is too large
Load Diff
189247
app/src/main/assets/pt/words.txt
Normal file
189247
app/src/main/assets/pt/words.txt
Normal file
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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user