Added sentences capitalization support (without pressing enter)

This commit is contained in:
merkost 2023-05-20 16:49:09 +10:00
parent 577553db15
commit 95f05a7b96
3 changed files with 91 additions and 14 deletions

View File

@ -1,9 +1,49 @@
package com.simplemobiletools.keyboard.helpers package com.simplemobiletools.keyboard.helpers
import android.content.Context
import com.simplemobiletools.keyboard.extensions.config
import com.simplemobiletools.keyboard.helpers.MyKeyboard.Companion.KEYCODE_SPACE
enum class ShiftState { enum class ShiftState {
OFF, OFF,
ON_ONE_CHAR, ON_ONE_CHAR,
ON_PERMANENT; ON_PERMANENT;
companion object {
private val endOfSentenceChars: List<Char> = listOf('.', '?', '!')
fun getDefaultShiftState(context: Context): ShiftState {
return when (context.config.enableSentencesCapitalization) {
true -> ON_ONE_CHAR
else -> OFF
}
}
fun getShiftStateForText(context: Context, newText: CharSequence?): ShiftState {
if (context.config.enableSentencesCapitalization.not()) return OFF
val twoLastSymbols = newText?.takeLast(2)
return when {
twoLastSymbols?.getOrNull(1) == KEYCODE_SPACE.toChar() && endOfSentenceChars.contains(twoLastSymbols.getOrNull(0)) -> {
ON_ONE_CHAR
}
else -> OFF
}
}
fun shouldCapitalizeSentence(previousChar: Char?, currentChar: Char): Boolean {
return currentChar.code == KEYCODE_SPACE && endOfSentenceChars.contains(previousChar)
}
fun shouldCapitalizeOnDelete(text: CharSequence?): Boolean {
if (text.isNullOrEmpty()) {
return true
}
return shouldCapitalizeSentence(currentChar = text.last(), previousChar = text.getOrNull(text.lastIndex - 1))
}
}
} }
// limit the count of alternative characters that show up at long pressing a key // limit the count of alternative characters that show up at long pressing a key

View File

@ -255,13 +255,14 @@ class MyKeyboard {
* @param enterKeyType determines what icon should we show on Enter key * @param enterKeyType determines what icon should we show on Enter key
*/ */
@JvmOverloads @JvmOverloads
constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int) { constructor(context: Context, @XmlRes xmlLayoutResId: Int, enterKeyType: Int, shiftState: ShiftState = ShiftState.OFF) {
mDisplayWidth = context.resources.displayMetrics.widthPixels mDisplayWidth = context.resources.displayMetrics.widthPixels
mDefaultHorizontalGap = 0 mDefaultHorizontalGap = 0
mDefaultWidth = mDisplayWidth / 10 mDefaultWidth = mDisplayWidth / 10
mDefaultHeight = mDefaultWidth mDefaultHeight = mDefaultWidth
mKeyboardHeightMultiplier = getKeyboardHeightMultiplier(context.config.keyboardHeightMultiplier) mKeyboardHeightMultiplier = getKeyboardHeightMultiplier(context.config.keyboardHeightMultiplier)
mKeys = ArrayList() mKeys = ArrayList()
mShiftState = shiftState
mEnterKeyType = enterKeyType mEnterKeyType = enterKeyType
loadKeyboard(context, context.resources.getXml(xmlLayoutResId)) loadKeyboard(context, context.resources.getXml(xmlLayoutResId))
} }

View File

@ -20,7 +20,8 @@ import com.simplemobiletools.keyboard.R
import com.simplemobiletools.keyboard.extensions.config import com.simplemobiletools.keyboard.extensions.config
import com.simplemobiletools.keyboard.helpers.* import com.simplemobiletools.keyboard.helpers.*
import com.simplemobiletools.keyboard.views.MyKeyboardView import com.simplemobiletools.keyboard.views.MyKeyboardView
import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.* import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.keyboard_holder
import kotlinx.android.synthetic.main.keyboard_view_keyboard.view.keyboard_view
// based on https://www.androidauthority.com/lets-build-custom-keyboard-android-832362/ // based on https://www.androidauthority.com/lets-build-custom-keyboard-android-832362/
class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionListener, SharedPreferences.OnSharedPreferenceChangeListener { class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionListener, SharedPreferences.OnSharedPreferenceChangeListener {
@ -41,7 +42,6 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
override fun onInitializeInterface() { override fun onInitializeInterface() {
super.onInitializeInterface() super.onInitializeInterface()
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType)
getSharedPrefs().registerOnSharedPreferenceChangeListener(this) getSharedPrefs().registerOnSharedPreferenceChangeListener(this)
} }
@ -65,8 +65,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
super.onStartInput(attribute, restarting) super.onStartInput(attribute, restarting)
inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS
enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION) enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION)
keyboard = createNewKeyboard()
keyboard = getKeyBoard()
keyboardView?.setKeyboard(keyboard!!) keyboardView?.setKeyboard(keyboard!!)
keyboardView?.setEditorInfo(attribute) keyboardView?.setEditorInfo(attribute)
updateShiftKeyState() updateShiftKeyState()
@ -97,7 +96,14 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
when (code) { when (code) {
MyKeyboard.KEYCODE_DELETE -> { MyKeyboard.KEYCODE_DELETE -> {
if (keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR) { if (keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR) {
keyboard!!.mShiftState = ShiftState.OFF keyboard!!.setShifted(ShiftState.OFF)
}
if (config.enableSentencesCapitalization) {
val extractedText = inputConnection.getTextBeforeCursor(3, 0)?.dropLast(1)
if (ShiftState.shouldCapitalizeOnDelete(text = extractedText)) {
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
}
} }
val selectedText = inputConnection.getSelectedText(0) val selectedText = inputConnection.getSelectedText(0)
@ -109,6 +115,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} }
keyboardView!!.invalidateAllKeys() keyboardView!!.invalidateAllKeys()
} }
MyKeyboard.KEYCODE_SHIFT -> { MyKeyboard.KEYCODE_SHIFT -> {
if (keyboardMode == KEYBOARD_LETTERS) { if (keyboardMode == KEYBOARD_LETTERS) {
when { when {
@ -132,6 +139,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} }
keyboardView!!.invalidateAllKeys() keyboardView!!.invalidateAllKeys()
} }
MyKeyboard.KEYCODE_ENTER -> { MyKeyboard.KEYCODE_ENTER -> {
val imeOptionsActionId = getImeOptionsActionId() val imeOptionsActionId = getImeOptionsActionId()
if (imeOptionsActionId != IME_ACTION_NONE) { if (imeOptionsActionId != IME_ACTION_NONE) {
@ -139,8 +147,13 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} else { } else {
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)) inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)) inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
if (config.enableSentencesCapitalization) {
keyboard!!.mShiftState = ShiftState.ON_ONE_CHAR
} }
} }
}
MyKeyboard.KEYCODE_MODE_CHANGE -> { MyKeyboard.KEYCODE_MODE_CHANGE -> {
val keyboardXml = if (keyboardMode == KEYBOARD_LETTERS) { val keyboardXml = if (keyboardMode == KEYBOARD_LETTERS) {
keyboardMode = KEYBOARD_SYMBOLS keyboardMode = KEYBOARD_SYMBOLS
@ -152,11 +165,15 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
keyboard = MyKeyboard(this, keyboardXml, enterKeyType) keyboard = MyKeyboard(this, keyboardXml, enterKeyType)
keyboardView!!.setKeyboard(keyboard!!) keyboardView!!.setKeyboard(keyboard!!)
} }
MyKeyboard.KEYCODE_EMOJI -> { MyKeyboard.KEYCODE_EMOJI -> {
keyboardView?.openEmojiPalette() keyboardView?.openEmojiPalette()
} }
else -> { else -> {
var codeChar = code.toChar() var codeChar = code.toChar()
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
if (Character.isLetter(codeChar) && keyboard!!.mShiftState > ShiftState.OFF) { if (Character.isLetter(codeChar) && keyboard!!.mShiftState > ShiftState.OFF) {
codeChar = Character.toUpperCase(codeChar) codeChar = Character.toUpperCase(codeChar)
} }
@ -165,18 +182,28 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
// However, avoid doing that in cases when the EditText for example requires numbers as the input. // However, avoid doing that in cases when the EditText for example requires numbers as the input.
// We can detect that by the text not changing on pressing Space. // We can detect that by the text not changing on pressing Space.
if (keyboardMode != KEYBOARD_LETTERS && code == MyKeyboard.KEYCODE_SPACE) { if (keyboardMode != KEYBOARD_LETTERS && code == MyKeyboard.KEYCODE_SPACE) {
val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return
inputConnection.commitText(codeChar.toString(), 1) inputConnection.commitText(codeChar.toString(), 1)
val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text
switchToLetters = originalText != newText if (originalText != newText) {
switchToLetters = true
}
} else { } else {
inputConnection.commitText(codeChar.toString(), 1) inputConnection.commitText(codeChar.toString(), 1)
} }
if (keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR && keyboardMode == KEYBOARD_LETTERS) { if (keyboardMode == KEYBOARD_LETTERS) {
keyboard!!.mShiftState = ShiftState.OFF if (keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR) {
keyboard!!.setShifted(ShiftState.OFF)
}
if (config.enableSentencesCapitalization && ShiftState.shouldCapitalizeSentence(
previousChar = originalText.lastOrNull(), currentChar = code.toChar()
)
) {
keyboard!!.setShifted(ShiftState.ON_ONE_CHAR)
}
keyboardView!!.invalidateAllKeys() keyboardView!!.invalidateAllKeys()
} }
} }
} }
@ -187,8 +214,12 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
override fun onActionUp() { override fun onActionUp() {
if (switchToLetters) { if (switchToLetters) {
// TODO: Change keyboardMode to enum class
keyboardMode = KEYBOARD_LETTERS keyboardMode = KEYBOARD_LETTERS
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType) val text = currentInputConnection?.getExtractedText(ExtractedTextRequest(), 0)?.text
val newShiftState = ShiftState.getShiftStateForText(this, text)
keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType, shiftState = newShiftState)
val editorInfo = currentInputEditorInfo val editorInfo = currentInputEditorInfo
if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != ShiftState.ON_PERMANENT) { if (editorInfo != null && editorInfo.inputType != InputType.TYPE_NULL && keyboard?.mShiftState != ShiftState.ON_PERMANENT) {
@ -215,32 +246,37 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL
} }
override fun reloadKeyboard() { override fun reloadKeyboard() {
val keyboard = getKeyBoard() val keyboard = createNewKeyboard()
this.keyboard = keyboard this.keyboard = keyboard
keyboardView?.setKeyboard(keyboard) keyboardView?.setKeyboard(keyboard)
} }
private fun getKeyBoard(): MyKeyboard { private fun createNewKeyboard(): MyKeyboard {
val keyboardXml = when (inputTypeClass) { val keyboardXml = when (inputTypeClass) {
TYPE_CLASS_NUMBER -> { TYPE_CLASS_NUMBER -> {
keyboardMode = KEYBOARD_NUMBERS keyboardMode = KEYBOARD_NUMBERS
R.xml.keys_numbers R.xml.keys_numbers
} }
TYPE_CLASS_PHONE -> { TYPE_CLASS_PHONE -> {
keyboardMode = KEYBOARD_PHONE keyboardMode = KEYBOARD_PHONE
R.xml.keys_phone R.xml.keys_phone
} }
TYPE_CLASS_DATETIME -> { TYPE_CLASS_DATETIME -> {
keyboardMode = KEYBOARD_SYMBOLS keyboardMode = KEYBOARD_SYMBOLS
R.xml.keys_symbols R.xml.keys_symbols
} }
else -> { else -> {
keyboardMode = KEYBOARD_LETTERS keyboardMode = KEYBOARD_LETTERS
getKeyboardLayoutXML() getKeyboardLayoutXML()
} }
} }
return MyKeyboard(this, keyboardXml, enterKeyType) return MyKeyboard(
context = this, xmlLayoutResId = keyboardXml, enterKeyType = enterKeyType, shiftState = ShiftState.getDefaultShiftState(this)
)
} }
override fun onUpdateSelection(oldSelStart: Int, oldSelEnd: Int, newSelStart: Int, newSelEnd: Int, candidatesStart: Int, candidatesEnd: Int) { override fun onUpdateSelection(oldSelStart: Int, oldSelEnd: Int, newSelStart: Int, newSelEnd: Int, candidatesStart: Int, candidatesEnd: Int) {