From 95f05a7b964a39e61f06f581cc7499ab1e698319 Mon Sep 17 00:00:00 2001 From: merkost Date: Sat, 20 May 2023 16:49:09 +1000 Subject: [PATCH] Added sentences capitalization support (without pressing enter) --- .../keyboard/helpers/Constants.kt | 40 ++++++++++++ .../keyboard/helpers/MyKeyboard.kt | 3 +- .../keyboard/services/SimpleKeyboardIME.kt | 62 +++++++++++++++---- 3 files changed, 91 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt index d836eb9..8d7cf90 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt @@ -1,9 +1,49 @@ 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 { OFF, ON_ONE_CHAR, ON_PERMANENT; + + companion object { + private val endOfSentenceChars: List = 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 diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/MyKeyboard.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/MyKeyboard.kt index 97ccc3c..0a8a0f2 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/MyKeyboard.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/MyKeyboard.kt @@ -255,13 +255,14 @@ class MyKeyboard { * @param enterKeyType determines what icon should we show on Enter key */ @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 mDefaultHorizontalGap = 0 mDefaultWidth = mDisplayWidth / 10 mDefaultHeight = mDefaultWidth mKeyboardHeightMultiplier = getKeyboardHeightMultiplier(context.config.keyboardHeightMultiplier) mKeys = ArrayList() + mShiftState = shiftState mEnterKeyType = enterKeyType loadKeyboard(context, context.resources.getXml(xmlLayoutResId)) } diff --git a/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt b/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt index 1fdc9ec..7dbcc29 100644 --- a/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt +++ b/app/src/main/kotlin/com/simplemobiletools/keyboard/services/SimpleKeyboardIME.kt @@ -20,7 +20,8 @@ import com.simplemobiletools.keyboard.R import com.simplemobiletools.keyboard.extensions.config import com.simplemobiletools.keyboard.helpers.* 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/ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionListener, SharedPreferences.OnSharedPreferenceChangeListener { @@ -41,7 +42,6 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL override fun onInitializeInterface() { super.onInitializeInterface() - keyboard = MyKeyboard(this, getKeyboardLayoutXML(), enterKeyType) getSharedPrefs().registerOnSharedPreferenceChangeListener(this) } @@ -65,8 +65,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL super.onStartInput(attribute, restarting) inputTypeClass = attribute!!.inputType and TYPE_MASK_CLASS enterKeyType = attribute.imeOptions and (IME_MASK_ACTION or IME_FLAG_NO_ENTER_ACTION) - - keyboard = getKeyBoard() + keyboard = createNewKeyboard() keyboardView?.setKeyboard(keyboard!!) keyboardView?.setEditorInfo(attribute) updateShiftKeyState() @@ -97,7 +96,14 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL when (code) { MyKeyboard.KEYCODE_DELETE -> { 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) @@ -109,6 +115,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL } keyboardView!!.invalidateAllKeys() } + MyKeyboard.KEYCODE_SHIFT -> { if (keyboardMode == KEYBOARD_LETTERS) { when { @@ -132,6 +139,7 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL } keyboardView!!.invalidateAllKeys() } + MyKeyboard.KEYCODE_ENTER -> { val imeOptionsActionId = getImeOptionsActionId() if (imeOptionsActionId != IME_ACTION_NONE) { @@ -139,8 +147,13 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL } else { inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, 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 -> { val keyboardXml = if (keyboardMode == KEYBOARD_LETTERS) { keyboardMode = KEYBOARD_SYMBOLS @@ -152,11 +165,15 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL keyboard = MyKeyboard(this, keyboardXml, enterKeyType) keyboardView!!.setKeyboard(keyboard!!) } + MyKeyboard.KEYCODE_EMOJI -> { keyboardView?.openEmojiPalette() } + else -> { var codeChar = code.toChar() + val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return + if (Character.isLetter(codeChar) && keyboard!!.mShiftState > ShiftState.OFF) { 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. // We can detect that by the text not changing on pressing Space. if (keyboardMode != KEYBOARD_LETTERS && code == MyKeyboard.KEYCODE_SPACE) { - val originalText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text ?: return inputConnection.commitText(codeChar.toString(), 1) val newText = inputConnection.getExtractedText(ExtractedTextRequest(), 0)?.text - switchToLetters = originalText != newText + if (originalText != newText) { + switchToLetters = true + } } else { inputConnection.commitText(codeChar.toString(), 1) } - if (keyboard!!.mShiftState == ShiftState.ON_ONE_CHAR && keyboardMode == KEYBOARD_LETTERS) { - keyboard!!.mShiftState = ShiftState.OFF + if (keyboardMode == KEYBOARD_LETTERS) { + 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() } + } } @@ -187,8 +214,12 @@ class SimpleKeyboardIME : InputMethodService(), MyKeyboardView.OnKeyboardActionL override fun onActionUp() { if (switchToLetters) { + // TODO: Change keyboardMode to enum class 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 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() { - val keyboard = getKeyBoard() + val keyboard = createNewKeyboard() this.keyboard = keyboard keyboardView?.setKeyboard(keyboard) } - private fun getKeyBoard(): MyKeyboard { + private fun createNewKeyboard(): MyKeyboard { val keyboardXml = when (inputTypeClass) { TYPE_CLASS_NUMBER -> { keyboardMode = KEYBOARD_NUMBERS R.xml.keys_numbers } + TYPE_CLASS_PHONE -> { keyboardMode = KEYBOARD_PHONE R.xml.keys_phone } + TYPE_CLASS_DATETIME -> { keyboardMode = KEYBOARD_SYMBOLS R.xml.keys_symbols } + else -> { keyboardMode = KEYBOARD_LETTERS 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) {